diff --git a/src/debugpy/adapter/clients.py b/src/debugpy/adapter/clients.py index 39ae6b65..8d0fc703 100644 --- a/src/debugpy/adapter/clients.py +++ b/src/debugpy/adapter/clients.py @@ -284,10 +284,43 @@ class Client(components.Component): if self.session.no_debug: raise request.isnt_valid('"noDebug" is not supported for "attach"') - listen = request("listen", False) - if listen: - host = request("host", "127.0.0.1") - port = request("port", int) + host = request("host", unicode, optional=True) + port = request("port", int, optional=True) + listen = request("listen", dict, optional=True) + connect = request("connect", dict, optional=True) + pid = request("processId", (int, unicode), optional=True) + sub_pid = request("subProcessId", int, optional=True) + + if host != () or port != (): + if listen != (): + raise request.isnt_valid( + '"listen" and "host"/"port" are mutually exclusive' + ) + if connect != (): + raise request.isnt_valid( + '"connect" and "host"/"port" are mutually exclusive' + ) + if listen != (): + if connect != (): + raise request.isnt_valid( + '"listen" and "connect" are mutually exclusive' + ) + if pid != (): + raise request.isnt_valid( + '"listen" and "processId" are mutually exclusive' + ) + if sub_pid != (): + raise request.isnt_valid( + '"listen" and "subProcessId" are mutually exclusive' + ) + if pid != () and sub_pid != (): + raise request.isnt_valid( + '"processId" and "subProcessId" are mutually exclusive' + ) + + if listen != (): + host = listen("host", "127.0.0.1") + port = listen("port", int) adapter.access_token = None host, port = servers.serve(host, port) else: @@ -303,30 +336,22 @@ class Client(components.Component): # in response to a "debugpyAttach" event. If so, the debug server should be # connected already, and thus the wait timeout is zero. # - # If neither is specified, and "listen" is true, this is attach-by-socket - # with the server expected to connect to the adapter via debugpy.connect(). There - # is no PID known in advance, so just wait until the first server connection - # indefinitely, with no timeout. + # If "listen" is specified, this is attach-by-socket with the server expected + # to connect to the adapter via debugpy.connect(). There is no PID known in + # advance, so just wait until the first server connection indefinitely, with + # no timeout. # - # If neither is specified, and "listen" is false, this is attach-by-socket - # in which the server has spawned the adapter via debugpy.listen(). There - # is no PID known to the client in advance, but the server connection should be - # either be there already, or the server should be connecting shortly, so there - # must be a timeout. + # If "connect" is specified, this is attach-by-socket in which the server has + # spawned the adapter via debugpy.listen(). There is no PID known to the client + # in advance, but the server connection should be either be there already, or + # the server should be connecting shortly, so there must be a timeout. # # In the last two cases, if there's more than one server connection already, # this is a multiprocess re-attach. The client doesn't know the PID, so we just # connect it to the oldest server connection that we have - in most cases, it # will be the one for the root debuggee process, but if it has exited already, # it will be some subprocess. - - pid = request("processId", (int, unicode), optional=True) - sub_pid = request("subProcessId", int, optional=True) if pid != (): - if sub_pid != (): - raise request.isnt_valid( - '"processId" and "subProcessId" are mutually exclusive' - ) if not isinstance(pid, int): try: pid = int(pid) @@ -339,7 +364,7 @@ class Client(components.Component): else: if sub_pid == (): pred = lambda conn: True - timeout = None if listen else 10 + timeout = 10 if listen == () else None else: pred = lambda conn: conn.pid == sub_pid timeout = 0 @@ -447,16 +472,21 @@ class Client(components.Component): self._known_subprocesses.add(conn) body.pop("processId", None) - if body.pop("listen", False): - body.pop("host", None) - body.pop("port", None) + body.pop("listen", None) body["name"] = fmt("Subprocess {0}", conn.pid) body["request"] = "attach" body["subProcessId"] = conn.pid - if "host" not in body: - body["host"] = "127.0.0.1" - if "port" not in body: - _, body["port"] = listener.getsockname() + + host = body.pop("host", None) + port = body.pop("port", None) + if "connect" not in body: + body["connect"] = {} + if "host" not in body["connect"]: + body["connect"]["host"] = host if host is not None else "127.0.0.1" + if "port" not in body["connect"]: + if port is None: + _, port = listener.getsockname() + body["connect"]["port"] = port self.channel.send_event("debugpyAttach", body) diff --git a/tests/debug/config.py b/tests/debug/config.py index d42e7c81..d6203b54 100644 --- a/tests/debug/config.py +++ b/tests/debug/config.py @@ -63,9 +63,8 @@ class DebugConfig(collections.MutableMapping): "waitOnNormalExit": False, "waitOnAbnormalExit": False, # Attach by socket - "host": (), - "port": (), - "listen": False, + "listen": (), + "connect": (), # Attach by PID "processId": (), } diff --git a/tests/debug/runners.py b/tests/debug/runners.py index b9c6fe73..6014f0ae 100644 --- a/tests/debug/runners.py +++ b/tests/debug/runners.py @@ -215,8 +215,9 @@ def attach_connect(session, target, method, cwd=None, wait=True, log_dir=None): assert method in ("api", "cli") config = _attach_common_config(session, target, cwd) - config["host"] = host = attach_connect.host - config["port"] = port = attach_connect.port + config["connect"] = {} + config["connect"]["host"] = host = attach_connect.host + config["connect"]["port"] = port = attach_connect.port if method == "cli": args = [ @@ -267,9 +268,9 @@ def attach_listen(session, target, method, cwd=None, log_dir=None): assert method in ("api", "cli") config = _attach_common_config(session, target, cwd) - config["listen"] = True - config["host"] = host = attach_listen.host - config["port"] = port = attach_listen.port + config["listen"] = {} + config["listen"]["host"] = host = attach_listen.host + config["listen"]["port"] = port = attach_listen.port if method == "cli": args = [ diff --git a/tests/debug/session.py b/tests/debug/session.py index d07a075a..441eb164 100644 --- a/tests/debug/session.py +++ b/tests/debug/session.py @@ -432,8 +432,8 @@ class Session(object): config = self.config request = config.get("request", None) if request == "attach": - host = config["host"] - port = config["port"] + host = config["connect"]["host"] + port = config["connect"]["port"] self.connect_to_adapter((host, port)) return self.request_attach() else: diff --git a/tests/debugpy/test_attach.py b/tests/debugpy/test_attach.py index 5c8b8864..bf11ae20 100644 --- a/tests/debugpy/test_attach.py +++ b/tests/debugpy/test_attach.py @@ -54,7 +54,7 @@ def test_attach_api(pyfile, target, wait_for_client, is_client_connected, stop_m with debug.Session() as session: host, port = runners.attach_connect.host, runners.attach_connect.port - session.config.update({"host": host, "port": port}) + session.config.update({"connect": {"host": host, "port": port}}) backchannel = session.open_backchannel() session.spawn_debuggee( @@ -128,9 +128,9 @@ def test_reattach(pyfile, target, run): with debug.Session() as session2: session2.config.update(session1.config) - if "host" in session2.config: + if "connect" in session2.config: session2.connect_to_adapter( - (session2.config["host"], session2.config["port"]) + (session2.config["connect"]["host"], session2.config["connect"]["port"]) ) with session2.request_attach(): diff --git a/tests/debugpy/test_multiproc.py b/tests/debugpy/test_multiproc.py index 4148cc13..67b6f3af 100644 --- a/tests/debugpy/test_multiproc.py +++ b/tests/debugpy/test_multiproc.py @@ -109,8 +109,10 @@ def test_multiprocessing(pyfile, target, run, start_method): "name": some.str, "request": "attach", "subProcessId": some.int, - "host": some.str, - "port": some.int, + "connect": { + "host": some.str, + "port": some.int, + } } ) @@ -129,8 +131,10 @@ def test_multiprocessing(pyfile, target, run, start_method): "name": some.str, "request": "attach", "subProcessId": some.int, - "host": some.str, - "port": some.int, + "connect": { + "host": some.str, + "port": some.int, + } } ) @@ -191,8 +195,10 @@ def test_subprocess(pyfile, target, run): "name": some.str, "request": "attach", "subProcessId": some.int, - "host": some.str, - "port": some.int, + "connect": { + "host": some.str, + "port": some.int, + } } )