Use "connect":{...} and "listen":{...} to indicate direction of connection in debug configuration.

This commit is contained in:
Pavel Minaev 2020-03-04 16:04:59 -08:00 committed by Pavel Minaev
parent 2d013e58ec
commit dccb5e6198
6 changed files with 83 additions and 47 deletions

View file

@ -284,10 +284,43 @@ class Client(components.Component):
if self.session.no_debug: if self.session.no_debug:
raise request.isnt_valid('"noDebug" is not supported for "attach"') raise request.isnt_valid('"noDebug" is not supported for "attach"')
listen = request("listen", False) host = request("host", unicode, optional=True)
if listen: port = request("port", int, optional=True)
host = request("host", "127.0.0.1") listen = request("listen", dict, optional=True)
port = request("port", int) 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 adapter.access_token = None
host, port = servers.serve(host, port) host, port = servers.serve(host, port)
else: else:
@ -303,30 +336,22 @@ class Client(components.Component):
# in response to a "debugpyAttach" event. If so, the debug server should be # in response to a "debugpyAttach" event. If so, the debug server should be
# connected already, and thus the wait timeout is zero. # connected already, and thus the wait timeout is zero.
# #
# If neither is specified, and "listen" is true, this is attach-by-socket # If "listen" is specified, this is attach-by-socket with the server expected
# with the server expected to connect to the adapter via debugpy.connect(). There # to connect to the adapter via debugpy.connect(). There is no PID known in
# is no PID known in advance, so just wait until the first server connection # advance, so just wait until the first server connection indefinitely, with
# indefinitely, with no timeout. # no timeout.
# #
# If neither is specified, and "listen" is false, this is attach-by-socket # If "connect" is specified, this is attach-by-socket in which the server has
# in which the server has spawned the adapter via debugpy.listen(). There # spawned the adapter via debugpy.listen(). There is no PID known to the client
# is no PID known to the client in advance, but the server connection should be # in advance, but the server connection should be either be there already, or
# either be there already, or the server should be connecting shortly, so there # the server should be connecting shortly, so there must be a timeout.
# must be a timeout.
# #
# In the last two cases, if there's more than one server connection already, # 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 # 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 # 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, # will be the one for the root debuggee process, but if it has exited already,
# it will be some subprocess. # it will be some subprocess.
pid = request("processId", (int, unicode), optional=True)
sub_pid = request("subProcessId", int, optional=True)
if pid != (): if pid != ():
if sub_pid != ():
raise request.isnt_valid(
'"processId" and "subProcessId" are mutually exclusive'
)
if not isinstance(pid, int): if not isinstance(pid, int):
try: try:
pid = int(pid) pid = int(pid)
@ -339,7 +364,7 @@ class Client(components.Component):
else: else:
if sub_pid == (): if sub_pid == ():
pred = lambda conn: True pred = lambda conn: True
timeout = None if listen else 10 timeout = 10 if listen == () else None
else: else:
pred = lambda conn: conn.pid == sub_pid pred = lambda conn: conn.pid == sub_pid
timeout = 0 timeout = 0
@ -447,16 +472,21 @@ class Client(components.Component):
self._known_subprocesses.add(conn) self._known_subprocesses.add(conn)
body.pop("processId", None) body.pop("processId", None)
if body.pop("listen", False): body.pop("listen", None)
body.pop("host", None)
body.pop("port", None)
body["name"] = fmt("Subprocess {0}", conn.pid) body["name"] = fmt("Subprocess {0}", conn.pid)
body["request"] = "attach" body["request"] = "attach"
body["subProcessId"] = conn.pid body["subProcessId"] = conn.pid
if "host" not in body:
body["host"] = "127.0.0.1" host = body.pop("host", None)
if "port" not in body: port = body.pop("port", None)
_, body["port"] = listener.getsockname() 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) self.channel.send_event("debugpyAttach", body)

View file

@ -63,9 +63,8 @@ class DebugConfig(collections.MutableMapping):
"waitOnNormalExit": False, "waitOnNormalExit": False,
"waitOnAbnormalExit": False, "waitOnAbnormalExit": False,
# Attach by socket # Attach by socket
"host": (), "listen": (),
"port": (), "connect": (),
"listen": False,
# Attach by PID # Attach by PID
"processId": (), "processId": (),
} }

View file

@ -215,8 +215,9 @@ def attach_connect(session, target, method, cwd=None, wait=True, log_dir=None):
assert method in ("api", "cli") assert method in ("api", "cli")
config = _attach_common_config(session, target, cwd) config = _attach_common_config(session, target, cwd)
config["host"] = host = attach_connect.host config["connect"] = {}
config["port"] = port = attach_connect.port config["connect"]["host"] = host = attach_connect.host
config["connect"]["port"] = port = attach_connect.port
if method == "cli": if method == "cli":
args = [ args = [
@ -267,9 +268,9 @@ def attach_listen(session, target, method, cwd=None, log_dir=None):
assert method in ("api", "cli") assert method in ("api", "cli")
config = _attach_common_config(session, target, cwd) config = _attach_common_config(session, target, cwd)
config["listen"] = True config["listen"] = {}
config["host"] = host = attach_listen.host config["listen"]["host"] = host = attach_listen.host
config["port"] = port = attach_listen.port config["listen"]["port"] = port = attach_listen.port
if method == "cli": if method == "cli":
args = [ args = [

View file

@ -432,8 +432,8 @@ class Session(object):
config = self.config config = self.config
request = config.get("request", None) request = config.get("request", None)
if request == "attach": if request == "attach":
host = config["host"] host = config["connect"]["host"]
port = config["port"] port = config["connect"]["port"]
self.connect_to_adapter((host, port)) self.connect_to_adapter((host, port))
return self.request_attach() return self.request_attach()
else: else:

View file

@ -54,7 +54,7 @@ def test_attach_api(pyfile, target, wait_for_client, is_client_connected, stop_m
with debug.Session() as session: with debug.Session() as session:
host, port = runners.attach_connect.host, runners.attach_connect.port 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() backchannel = session.open_backchannel()
session.spawn_debuggee( session.spawn_debuggee(
@ -128,9 +128,9 @@ def test_reattach(pyfile, target, run):
with debug.Session() as session2: with debug.Session() as session2:
session2.config.update(session1.config) session2.config.update(session1.config)
if "host" in session2.config: if "connect" in session2.config:
session2.connect_to_adapter( session2.connect_to_adapter(
(session2.config["host"], session2.config["port"]) (session2.config["connect"]["host"], session2.config["connect"]["port"])
) )
with session2.request_attach(): with session2.request_attach():

View file

@ -109,8 +109,10 @@ def test_multiprocessing(pyfile, target, run, start_method):
"name": some.str, "name": some.str,
"request": "attach", "request": "attach",
"subProcessId": some.int, "subProcessId": some.int,
"host": some.str, "connect": {
"port": some.int, "host": some.str,
"port": some.int,
}
} }
) )
@ -129,8 +131,10 @@ def test_multiprocessing(pyfile, target, run, start_method):
"name": some.str, "name": some.str,
"request": "attach", "request": "attach",
"subProcessId": some.int, "subProcessId": some.int,
"host": some.str, "connect": {
"port": some.int, "host": some.str,
"port": some.int,
}
} }
) )
@ -191,8 +195,10 @@ def test_subprocess(pyfile, target, run):
"name": some.str, "name": some.str,
"request": "attach", "request": "attach",
"subProcessId": some.int, "subProcessId": some.int,
"host": some.str, "connect": {
"port": some.int, "host": some.str,
"port": some.int,
}
} }
) )