From 03083c19731ee082f350473232234373426cfd72 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 30 Mar 2018 16:19:18 -0700 Subject: [PATCH 01/16] initial commit --- ptvsd/ipcjson.py | 2 ++ ptvsd/wrapper.py | 74 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/ptvsd/ipcjson.py b/ptvsd/ipcjson.py index 4759c37eb..1ff29941c 100644 --- a/ptvsd/ipcjson.py +++ b/ptvsd/ipcjson.py @@ -100,6 +100,7 @@ def _send(self, **payload): try: self.__socket.send(headers) self.__socket.send(content) + _trace('Sent content', content) except BrokenPipeError: pass except OSError as exc: @@ -191,6 +192,7 @@ def _wait_for_message(self): # read content, utf-8 encoded content = self._buffered_read_as_utf8(length) try: + _trace('Received content', content) msg = json.loads(content) self._receive_message(msg) except ValueError: diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 95803b234..64db25340 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -45,9 +45,9 @@ __version__ = "4.0.0a5" -#def ipcjson_trace(s): -# print(s) -#ipcjson._TRACE = ipcjson_trace +# def ipcjson_trace(s): +# print(s) +# ipcjson._TRACE = ipcjson_trace ptvsd_sys_exit_code = 0 WAIT_FOR_DISCONNECT_REQUEST_TIMEOUT = 2 @@ -233,8 +233,21 @@ def __init__(self, name, description, stack, source): self.stack = stack self.source = source - -class PydevdSocket(object): +class Observable(object): + def __init__(self): + self._observers = [] + + def register_observer(self, observer): + self._observers.append(observer) + + def un_register_observer(self, observer): + self._observers.remove(observer) + + def notify_observers(self, *args, **kwargs): + for observer in self._observers: + observer.notify(*args, **kwargs) + +class PydevdSocket(Observable): """A dummy socket-like object for communicating with pydevd. It parses pydevd messages and redirects them to the provided handler @@ -245,9 +258,10 @@ class PydevdSocket(object): _vscprocessor = None - def __init__(self, event_handler): + def __init__(self): + super(PydevdSocket, self).__init__() #self.log = open('pydevd.log', 'w') - self.event_handler = event_handler + self.event_handler = self.notify_observers self.lock = threading.Lock() self.seq = 1000000000 self.pipe_r, self.pipe_w = os.pipe() @@ -255,7 +269,7 @@ def __init__(self, event_handler): self._closed = False self._closing = False - + def close(self): """Mark the socket as closed and release any resources.""" if self._closing: @@ -621,6 +635,7 @@ def __init__(self, socket, pydevd, logfile=None, super(VSCodeMessageProcessor, self).__init__(socket=socket, own_socket=False, logfile=logfile) + pydevd.register_observer(self) self.socket = socket self.pydevd = pydevd self.killonclose = killonclose @@ -659,16 +674,18 @@ def __init__(self, socket, pydevd, logfile=None, # closing the adapter - def close(self): + def close(self, exit=True): """Stop the message processor and release its resources.""" if self._closed: return self._closed = True - # Stop the PyDevd message handler first. - self._stop_pydevd_message_loop() - # Treat PyDevd as effectively exited. - self._handle_pydevd_stopped() + if exit: + # Stop the PyDevd message handler first. + self._stop_pydevd_message_loop() + # Treat PyDevd as effectively exited. + self._handle_pydevd_stopped() + # Close the editor-side socket. self._stop_vsc_message_loop() @@ -689,6 +706,9 @@ def _stop_vsc_message_loop(self): except Exception: pass + def notify(self, *args, **kwargs): + self._on_pydevd_event(*args) + def _handle_pydevd_stopped(self): wait_on_normal_exit = self.debug_options.get( 'WAIT_ON_NORMAL_EXIT', False) @@ -792,7 +812,7 @@ def decorate(f): pydevd_events = EventHandlers() - def on_pydevd_event(self, cmd_id, seq, args): + def _on_pydevd_event(self, cmd_id, seq, args): # TODO: docstring try: f = self.pydevd_events[cmd_id] @@ -953,6 +973,7 @@ def on_disconnect(self, request, args): if self.start_reason == 'launch': self._handle_disconnect(request) else: + self.close(exit=False) self.send_response(request) def send_process_event(self, start_method): @@ -1714,23 +1735,22 @@ def _new_sock(): sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) return sock +def _add_socket_handler(client, pydevd, name, killonclose=True, addhandlers=True): + proc = VSCodeMessageProcessor(client, pydevd, killonclose=killonclose) -def _start(client, server, killonclose=True, addhandlers=True): - name = 'ptvsd.Client' if server is None else 'ptvsd.Server' - - pydevd = PydevdSocket(lambda *args: proc.on_pydevd_event(*args)) - proc = VSCodeMessageProcessor(client, pydevd, - killonclose=killonclose) - - server_thread = threading.Thread(target=proc.process_messages, - name=name) + server_thread = threading.Thread(target=proc.process_messages, name=name) server_thread.daemon = True server_thread.start() if addhandlers: _add_atexit_handler(proc, server_thread) _set_signal_handlers(proc) + +def _start(client, server, killonclose=True, addhandlers=True): + name = 'ptvsd.Client' if server is None else 'ptvsd.Server' + pydevd = PydevdSocket() + _add_socket_handler(client, pydevd, name, killonclose=killonclose, addhandlers=addhandlers) return pydevd @@ -1766,6 +1786,14 @@ def start_server(port, addhandlers=True): server = _create_server(port) client, _ = server.accept() pydevd = _start(client, server) + def _wait_for_more(): + while True: + client, _ = server.accept() + _add_socket_handler(client, pydevd, 'ptvsd.Server', killonclose=True, addhandlers=False) + + connection_thread = threading.Thread(target=_wait_for_more, name='ptvsd_client_connection') + connection_thread.daemon = True + connection_thread.start() return pydevd From 7d11fe6a55f23279205e26419aef25cd496cff88 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sat, 31 Mar 2018 15:42:33 -0700 Subject: [PATCH 02/16] refactor with signal handlers --- ptvsd/wrapper.py | 71 ++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 64db25340..1d8a17d13 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -233,20 +233,22 @@ def __init__(self, name, description, stack, source): self.stack = stack self.source = source + class Observable(object): def __init__(self): self._observers = [] - + def register_observer(self, observer): self._observers.append(observer) - + def un_register_observer(self, observer): self._observers.remove(observer) - + def notify_observers(self, *args, **kwargs): for observer in self._observers: observer.notify(*args, **kwargs) + class PydevdSocket(Observable): """A dummy socket-like object for communicating with pydevd. @@ -269,7 +271,7 @@ def __init__(self): self._closed = False self._closing = False - + def close(self): """Mark the socket as closed and release any resources.""" if self._closing: @@ -329,7 +331,7 @@ def recv(self, count): return b'' data = os.read(pipe_r, count) #self.log.write('>>>[' + data.decode('utf8') + ']\n\n') - #self.log.flush() + # self.log.flush() return data def recv_into(self, buf): @@ -359,7 +361,7 @@ def send(self, data): result = len(data) data = self._decode_and_unquote(data) #self.log.write('<<<[' + data + ']\n\n') - #self.log.flush() + # self.log.flush() cmd_id, seq, args = data.split('\t', 2) cmd_id = int(cmd_id) seq = int(seq) @@ -554,7 +556,7 @@ def get_sort_key(o): self.single_underscore.sort(key=get_sort_key) self.double_underscore.sort(key=get_sort_key) self.dunder.sort(key=get_sort_key) - #print('sorted') + # print('sorted') return self.variables + self.single_underscore + self.double_underscore + self.dunder # noqa @@ -716,7 +718,7 @@ def _handle_pydevd_stopped(self): 'WAIT_ON_ABNORMAL_EXIT', False) if (wait_on_normal_exit and not ptvsd_sys_exit_code) \ - or (wait_on_abnormal_exit and ptvsd_sys_exit_code): + or (wait_on_abnormal_exit and ptvsd_sys_exit_code): self.wait_on_exit_func() else: pass @@ -1433,7 +1435,7 @@ def on_setBreakpoints(self, request, args): for src_bp in src_bps: line = src_bp['line'] vsc_bpid = self.bp_map.add( - lambda vsc_bpid: (path, vsc_bpid)) + lambda vsc_bpid: (path, vsc_bpid)) self.path_casing.track_file_path_case(path) msg = msgfmt.format(vsc_bpid, bp_type, path, line, src_bp.get('condition', None)) @@ -1582,9 +1584,9 @@ def on_pydevd_thread_suspend(self, seq, args): pyd_tid = xml.thread['id'] reason = int(xml.thread['stop_reason']) STEP_REASONS = { - pydevd_comm.CMD_STEP_INTO, - pydevd_comm.CMD_STEP_OVER, - pydevd_comm.CMD_STEP_RETURN, + pydevd_comm.CMD_STEP_INTO, + pydevd_comm.CMD_STEP_OVER, + pydevd_comm.CMD_STEP_RETURN, } EXCEPTION_REASONS = { pydevd_comm.CMD_STEP_CAUGHT_EXCEPTION, @@ -1628,11 +1630,11 @@ def on_pydevd_thread_suspend(self, seq, args): text = unquote(xml.var[1]['type']) description = unquote(xml.var[1]['value']) frame_data = (( - unquote(f['file']), - int(f['line']), - unquote(f['name']), - None - ) for f in xframes) + unquote(f['file']), + int(f['line']), + unquote(f['name']), + None + ) for f in xframes) stack = ''.join(traceback.format_list(frame_data)) source = unquote(xframe['file']) except Exception: @@ -1735,7 +1737,8 @@ def _new_sock(): sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) return sock -def _add_socket_handler(client, pydevd, name, killonclose=True, addhandlers=True): + +def _add_pydevd_event_handler(client, pydevd, name, killonclose=True, addhandlers=True): proc = VSCodeMessageProcessor(client, pydevd, killonclose=killonclose) server_thread = threading.Thread(target=proc.process_messages, name=name) @@ -1745,12 +1748,12 @@ def _add_socket_handler(client, pydevd, name, killonclose=True, addhandlers=True if addhandlers: _add_atexit_handler(proc, server_thread) _set_signal_handlers(proc) - + + def _start(client, server, killonclose=True, addhandlers=True): name = 'ptvsd.Client' if server is None else 'ptvsd.Server' - pydevd = PydevdSocket() - _add_socket_handler(client, pydevd, name, killonclose=killonclose, addhandlers=addhandlers) + _add_pydevd_event_handler(client, pydevd, name, killonclose, addhandlers) return pydevd @@ -1762,14 +1765,21 @@ def handler(): atexit.register(handler) +signal_handler = None + + def _set_signal_handlers(proc): if platform.system() == 'Windows': return None - def handler(signum, frame): - proc.close() - sys.exit(0) - signal.signal(signal.SIGHUP, handler) + if signal_handler is None: + def handler(signum, frame): + signal_handler.notify_observers() + sys.exit(0) + signal.signal(signal.SIGHUP, handler) + signal_handler = Observable() + + signal_handler.register_observer({notify: lambda: proc.close()}) ######################## @@ -1786,12 +1796,14 @@ def start_server(port, addhandlers=True): server = _create_server(port) client, _ = server.accept() pydevd = _start(client, server) - def _wait_for_more(): + + def _wait_for_more_connections(): while True: client, _ = server.accept() - _add_socket_handler(client, pydevd, 'ptvsd.Server', killonclose=True, addhandlers=False) + _add_pydevd_event_handler(client, pydevd, name='ptvsd.Server') - connection_thread = threading.Thread(target=_wait_for_more, name='ptvsd_client_connection') + connection_thread = threading.Thread(target=_wait_for_more_connections, + name='ptvsd_client_connection') connection_thread.daemon = True connection_thread.start() return pydevd @@ -1807,7 +1819,8 @@ def start_client(host, port, addhandlers=True): """ client = _create_client() client.connect((host, port)) - pydevd = _start(client, None) + pydevd = PydevdSocket() + pydevd = _start(client, None, name='ptvsd.Client') return pydevd From ecbb0b4d06289a2fdb6122202b143db55bc1049b Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sat, 31 Mar 2018 19:52:46 -0700 Subject: [PATCH 03/16] more cleanup required --- ptvsd/wrapper.py | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 1d8a17d13..e0717f137 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -248,6 +248,10 @@ def notify_observers(self, *args, **kwargs): for observer in self._observers: observer.notify(*args, **kwargs) + @property + def observers_count(self): + return len(self._observers) + class PydevdSocket(Observable): """A dummy socket-like object for communicating with pydevd. @@ -678,6 +682,7 @@ def __init__(self, socket, pydevd, logfile=None, def close(self, exit=True): """Stop the message processor and release its resources.""" + self.pydevd.un_register_observer(self) if self._closed: return self._closed = True @@ -955,6 +960,14 @@ def _parse_debug_options(self, debug_options): @async_handler def on_attach(self, request, args): # TODO: docstring + if self.pydevd.observers_count > 1: + self.send_response( + request, + success=False, + message='Only one active connection is supported.', + ) + return + self.start_reason = 'attach' options = self.build_debug_options(args.get('debugOptions', [])) self.debug_options = self._parse_debug_options( @@ -1747,11 +1760,13 @@ def _add_pydevd_event_handler(client, pydevd, name, killonclose=True, addhandler if addhandlers: _add_atexit_handler(proc, server_thread) - _set_signal_handlers(proc) + _add_signal_handler(proc) def _start(client, server, killonclose=True, addhandlers=True): name = 'ptvsd.Client' if server is None else 'ptvsd.Server' + if addhandlers: + _set_signal_handler() pydevd = PydevdSocket() _add_pydevd_event_handler(client, pydevd, name, killonclose, addhandlers) return pydevd @@ -1765,21 +1780,25 @@ def handler(): atexit.register(handler) -signal_handler = None +signal_handler = Observable() -def _set_signal_handlers(proc): +def _set_signal_handler(): if platform.system() == 'Windows': return None + signal.signal(signal.SIGHUP, signal_sighup_handler) + + +def signal_sighup_handler(signum, frame): + signal_handler.notify_observers() + sys.exit(0) - if signal_handler is None: - def handler(signum, frame): - signal_handler.notify_observers() - sys.exit(0) - signal.signal(signal.SIGHUP, handler) - signal_handler = Observable() - signal_handler.register_observer({notify: lambda: proc.close()}) +def _add_signal_handler(proc): + if platform.system() == 'Windows': + return None + + signal_handler.register_observer({"notify": lambda: proc.close()}) ######################## From 2b40d99152e8f5a4fc8f394b4a51bad1dda9fbef Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sun, 1 Apr 2018 22:55:00 -0700 Subject: [PATCH 04/16] fix tests --- tests/highlevel/test_lifecycle.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/highlevel/test_lifecycle.py b/tests/highlevel/test_lifecycle.py index a170fdf00..10c0b36a9 100644 --- a/tests/highlevel/test_lifecycle.py +++ b/tests/highlevel/test_lifecycle.py @@ -90,15 +90,7 @@ def test_attach(self): self.new_event('initialized'), self.new_response(req_attach), self.new_response(req_config), - #self.new_event('process', **dict( - # name=sys.argv[0], - # systemProcessId=os.getpid(), - # isLocalProcess=True, - # startMethod='attach', - #)), self.new_response(req_disconnect), - self.new_event('exited', exitCode=0), - self.new_event('terminated'), ]) self.assert_received(self.debugger, [ self.debugger_msgs.new_request(CMD_VERSION, From 574b7ed1acbedf3d9528bd767c2def102b9cb2f7 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 09:04:14 -0700 Subject: [PATCH 05/16] undo unecessary formatting --- ptvsd/wrapper.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index e0717f137..8f7edb6d6 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -249,7 +249,7 @@ def notify_observers(self, *args, **kwargs): observer.notify(*args, **kwargs) @property - def observers_count(self): + def observer_count(self): return len(self._observers) @@ -335,7 +335,7 @@ def recv(self, count): return b'' data = os.read(pipe_r, count) #self.log.write('>>>[' + data.decode('utf8') + ']\n\n') - # self.log.flush() + #self.log.flush() return data def recv_into(self, buf): @@ -365,7 +365,7 @@ def send(self, data): result = len(data) data = self._decode_and_unquote(data) #self.log.write('<<<[' + data + ']\n\n') - # self.log.flush() + #self.log.flush() cmd_id, seq, args = data.split('\t', 2) cmd_id = int(cmd_id) seq = int(seq) @@ -560,7 +560,7 @@ def get_sort_key(o): self.single_underscore.sort(key=get_sort_key) self.double_underscore.sort(key=get_sort_key) self.dunder.sort(key=get_sort_key) - # print('sorted') + #print('sorted') return self.variables + self.single_underscore + self.double_underscore + self.dunder # noqa @@ -723,7 +723,7 @@ def _handle_pydevd_stopped(self): 'WAIT_ON_ABNORMAL_EXIT', False) if (wait_on_normal_exit and not ptvsd_sys_exit_code) \ - or (wait_on_abnormal_exit and ptvsd_sys_exit_code): + or (wait_on_abnormal_exit and ptvsd_sys_exit_code): self.wait_on_exit_func() else: pass @@ -960,7 +960,7 @@ def _parse_debug_options(self, debug_options): @async_handler def on_attach(self, request, args): # TODO: docstring - if self.pydevd.observers_count > 1: + if self.pydevd.observer_count > 1: self.send_response( request, success=False, @@ -1643,11 +1643,11 @@ def on_pydevd_thread_suspend(self, seq, args): text = unquote(xml.var[1]['type']) description = unquote(xml.var[1]['value']) frame_data = (( - unquote(f['file']), - int(f['line']), - unquote(f['name']), - None - ) for f in xframes) + unquote(f['file']), + int(f['line']), + unquote(f['name']), + None + ) for f in xframes) stack = ''.join(traceback.format_list(frame_data)) source = unquote(xframe['file']) except Exception: From 7a607e0b17c9993d4b5b62776bddd4550d5fbcfd Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 09:04:50 -0700 Subject: [PATCH 06/16] undo more unnecessary formatting --- ptvsd/wrapper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 8f7edb6d6..9fdbefc1c 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -45,9 +45,9 @@ __version__ = "4.0.0a5" -# def ipcjson_trace(s): -# print(s) -# ipcjson._TRACE = ipcjson_trace +#def ipcjson_trace(s): +# print(s) +#ipcjson._TRACE = ipcjson_trace ptvsd_sys_exit_code = 0 WAIT_FOR_DISCONNECT_REQUEST_TIMEOUT = 2 From aa5026190299077a265a978bc7d03b00576090eb Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 10:31:50 -0700 Subject: [PATCH 07/16] remove unwanted arg --- ptvsd/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 9fdbefc1c..ad824e9bd 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -1839,7 +1839,7 @@ def start_client(host, port, addhandlers=True): client = _create_client() client.connect((host, port)) pydevd = PydevdSocket() - pydevd = _start(client, None, name='ptvsd.Client') + pydevd = _start(client, None) return pydevd From fbce1db113c68d02e37b8a3153bfad9381036cc5 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 10:34:48 -0700 Subject: [PATCH 08/16] code review fixes --- ptvsd/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index ad824e9bd..715a90d67 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -1822,7 +1822,7 @@ def _wait_for_more_connections(): _add_pydevd_event_handler(client, pydevd, name='ptvsd.Server') connection_thread = threading.Thread(target=_wait_for_more_connections, - name='ptvsd_client_connection') + name='ptvsd.Client.Connection') connection_thread.daemon = True connection_thread.start() return pydevd From 8da8783fe85a2de810fc5228b0ff980b4ced47d9 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 10:51:40 -0700 Subject: [PATCH 09/16] refactor code --- ptvsd/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 715a90d67..802c0fee7 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -682,11 +682,11 @@ def __init__(self, socket, pydevd, logfile=None, def close(self, exit=True): """Stop the message processor and release its resources.""" - self.pydevd.un_register_observer(self) if self._closed: return self._closed = True + self.pydevd.un_register_observer(self) if exit: # Stop the PyDevd message handler first. self._stop_pydevd_message_loop() From 2d6ec0ae00a45343a17e959523e0e058fe7ee841 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 11:12:54 -0700 Subject: [PATCH 10/16] fixed test --- ptvsd/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 802c0fee7..a5aa27be0 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -988,8 +988,8 @@ def on_disconnect(self, request, args): if self.start_reason == 'launch': self._handle_disconnect(request) else: - self.close(exit=False) self.send_response(request) + self.close(exit=False) def send_process_event(self, start_method): # TODO: docstring From 23c0bb30cb0a78229ba95bf97d8c52d102af8a8b Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 11:54:41 -0700 Subject: [PATCH 11/16] send terminated event --- ptvsd/wrapper.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index a5aa27be0..4866ff94f 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -692,6 +692,11 @@ def close(self, exit=True): self._stop_pydevd_message_loop() # Treat PyDevd as effectively exited. self._handle_pydevd_stopped() + else: + # Notify the editor that the debugger has stopped. + self.send_event('terminated') + # The editor will send a "disconnect" request at this point. + self._wait_for_disconnect() # Close the editor-side socket. self._stop_vsc_message_loop() @@ -750,13 +755,13 @@ def _wait_for_disconnect(self, timeout=None): self.send_response(self.disconnect_request) self.disconnect_request = None - def _handle_disconnect(self, request): + def _handle_disconnect(self, request, exit=True): self.disconnect_request = request self.disconnect_request_event.set() killProcess = not self._closed - self.close() + self.close(exit=exit) # TODO: Move killing the process to close()? - if killProcess and self.killonclose: + if exit and killProcess and self.killonclose: os.kill(os.getpid(), signal.SIGTERM) # async helpers @@ -988,8 +993,7 @@ def on_disconnect(self, request, args): if self.start_reason == 'launch': self._handle_disconnect(request) else: - self.send_response(request) - self.close(exit=False) + self._handle_disconnect(request, exit=False) def send_process_event(self, start_method): # TODO: docstring From a762f27a9bbe01403fbd71af3b5b37e3ac78df54 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 12:43:01 -0700 Subject: [PATCH 12/16] fixed attach tests --- tests/highlevel/test_lifecycle.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/highlevel/test_lifecycle.py b/tests/highlevel/test_lifecycle.py index 10c0b36a9..702e8dc7c 100644 --- a/tests/highlevel/test_lifecycle.py +++ b/tests/highlevel/test_lifecycle.py @@ -54,7 +54,7 @@ def test_attach(self): # end req_disconnect = self.send_request('disconnect') finally: - with self._fix.wait_for_events(['exited', 'terminated']): + with self._fix.wait_for_events(['terminated']): self.fix.close_ptvsd() daemon.close() @@ -90,6 +90,7 @@ def test_attach(self): self.new_event('initialized'), self.new_response(req_attach), self.new_response(req_config), + self.new_event('terminated'), self.new_response(req_disconnect), ]) self.assert_received(self.debugger, [ From b5e31aaec0bf92cb4a92a8c5eb81cd3aa5937609 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 12:48:16 -0700 Subject: [PATCH 13/16] capture info before replying back in attach request --- ptvsd/wrapper.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 4866ff94f..6d4fd200b 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -965,6 +965,11 @@ def _parse_debug_options(self, debug_options): @async_handler def on_attach(self, request, args): # TODO: docstring + self.start_reason = 'attach' + options = self.build_debug_options(args.get('debugOptions', [])) + self.debug_options = self._parse_debug_options( + args.get('options', options)) + if self.pydevd.observer_count > 1: self.send_response( request, @@ -973,10 +978,6 @@ def on_attach(self, request, args): ) return - self.start_reason = 'attach' - options = self.build_debug_options(args.get('debugOptions', [])) - self.debug_options = self._parse_debug_options( - args.get('options', options)) self.send_response(request) @async_handler From 418d48937eb79944469d00834ab51534ad5ddfe5 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 12:53:08 -0700 Subject: [PATCH 14/16] change message --- ptvsd/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 6d4fd200b..b74621d07 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -974,7 +974,7 @@ def on_attach(self, request, args): self.send_response( request, success=False, - message='Only one active connection is supported.', + message='A debugger is already attached to this process.', ) return From 2c3976dd1fbdb1c4d325cf664de85c6e39116b5a Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 12:54:06 -0700 Subject: [PATCH 15/16] remove duplicate initialization --- ptvsd/wrapper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index b74621d07..8cc4d29dc 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -1843,7 +1843,6 @@ def start_client(host, port, addhandlers=True): """ client = _create_client() client.connect((host, port)) - pydevd = PydevdSocket() pydevd = _start(client, None) return pydevd From d059ca8d41859fd2c68a5735d96646fa83893df2 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 2 Apr 2018 15:56:15 -0700 Subject: [PATCH 16/16] fixed format error --- ptvsd/wrapper.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 8cc4d29dc..fb4762ab7 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -1756,7 +1756,11 @@ def _new_sock(): return sock -def _add_pydevd_event_handler(client, pydevd, name, killonclose=True, addhandlers=True): +def _add_pydevd_event_handler(client, + pydevd, + name, + killonclose=True, + addhandlers=True): proc = VSCodeMessageProcessor(client, pydevd, killonclose=killonclose) server_thread = threading.Thread(target=proc.process_messages, name=name)