diff --git a/.vscode/launch.json b/.vscode/launch.json index 3d891db7..43e7322c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -52,6 +52,7 @@ "host": "127.0.0.1" }, "logToFile": true, + //"restart": true, "debugAdapterPath": "${workspaceFolder}/src/debugpy/adapter" }, { diff --git a/src/debugpy/adapter/clients.py b/src/debugpy/adapter/clients.py index 2ba9976e..ada460ed 100644 --- a/src/debugpy/adapter/clients.py +++ b/src/debugpy/adapter/clients.py @@ -44,6 +44,7 @@ class Client(components.Component): def __init__(self, sock): if sock == "stdio": log.info("Connecting to client over stdio...", self) + self.using_stdio = True stream = messaging.JsonIOStream.from_stdio() # Make sure that nothing else tries to interfere with the stdio streams # that are going to be used for DAP communication from now on. @@ -52,6 +53,7 @@ class Client(components.Component): sys.stdout = stdout = open(os.devnull, "w") atexit.register(stdout.close) else: + self.using_stdio = False stream = messaging.JsonIOStream.from_socket(sock) with sessions.Session() as session: @@ -69,6 +71,11 @@ class Client(components.Component): """The "launch" or "attach" request as received from the client. """ + self.restart_requested = False + """Whether the client requested the debug adapter to be automatically + restarted via "restart":true in the start request. + """ + self._initialize_request = None """The "initialize" request as received from the client, to propagate to the server later.""" @@ -471,6 +478,7 @@ class Client(components.Component): host = listen("host", "127.0.0.1") port = listen("port", int) adapter.access_token = None + self.restart_requested = request("restart", False) host, port = servers.serve(host, port) else: if not servers.is_serving(): @@ -651,6 +659,10 @@ class Client(components.Component): @message_handler def terminate_request(self, request): + # If user specifically requests to terminate, it means that they don't want + # debug session auto-restart kicking in. + self.restart_requested = False + if self._forward_terminate_request: # According to the spec, terminate should try to do a gracefull shutdown. # We do this in the server by interrupting the main thread with a Ctrl+C. @@ -665,11 +677,29 @@ class Client(components.Component): @message_handler def disconnect_request(self, request): + # If user specifically requests to disconnect, it means that they don't want + # debug session auto-restart kicking in. + self.restart_requested = False + terminate_debuggee = request("terminateDebuggee", bool, optional=True) if terminate_debuggee == (): terminate_debuggee = None self.session.finalize('client requested "disconnect"', terminate_debuggee) - return {} + request.respond({}) + + if self.using_stdio: + # There's no way for the client to reconnect to this adapter once it disconnects + # from this session, so close any remaining server connections. + servers.stop_serving() + log.info("{0} disconnected from stdio; closing remaining server connections.", self) + for conn in servers.connections(): + try: + conn.channel.close() + except Exception: + log.swallow_exception() + + def disconnect(self): + super().disconnect() def notify_of_subprocess(self, conn): log.info("{1} is a subprocess of {0}.", self, conn) @@ -689,7 +719,7 @@ class Client(components.Component): self.known_subprocesses.add(conn) self.session.notify_changed() - for key in "processId", "listen", "preLaunchTask", "postDebugTask", "request": + for key in "processId", "listen", "preLaunchTask", "postDebugTask", "request", "restart": body.pop(key, None) body["name"] = "Subprocess {0}".format(conn.pid) diff --git a/src/debugpy/adapter/sessions.py b/src/debugpy/adapter/sessions.py index ca6c5742..0abebcc8 100644 --- a/src/debugpy/adapter/sessions.py +++ b/src/debugpy/adapter/sessions.py @@ -226,8 +226,11 @@ class Session(util.Observable): if self.client.is_connected: # Tell the client that debugging is over, but don't close the channel until it # tells us to, via the "disconnect" request. + body = {} + if self.client.restart_requested: + body["restart"] = True try: - self.client.channel.send_event("terminated") + self.client.channel.send_event("terminated", body) except Exception: pass diff --git a/src/debugpy/common/log.py b/src/debugpy/common/log.py index 62815439..e7f5e3fb 100644 --- a/src/debugpy/common/log.py +++ b/src/debugpy/common/log.py @@ -321,6 +321,7 @@ def describe_environment(header): prefix = " " * len(prefix) report("System paths:\n") + report_paths("sys.executable") report_paths("sys.prefix") report_paths("sys.base_prefix") report_paths("sys.real_prefix")