Skip to content

Commit 5014f25

Browse files
authored
Properly update pydevd._settrace.called (#1751)
* Properly update pydevd._settrace.called * Change _settrace.called to _listen.called * Remove unnecessary underscore from listen * Fix lint issues * Update multiple listen test
1 parent 43f4102 commit 5014f25

File tree

2 files changed

+59
-15
lines changed

2 files changed

+59
-15
lines changed

src/debugpy/server/api.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,9 @@ def _settrace(*args, **kwargs):
4242
# The stdin in notification is not acted upon in debugpy, so, disable it.
4343
kwargs.setdefault("notify_stdin", False)
4444
try:
45-
return pydevd.settrace(*args, **kwargs)
45+
pydevd.settrace(*args, **kwargs)
4646
except Exception:
4747
raise
48-
else:
49-
_settrace.called = True
50-
51-
52-
_settrace.called = False
5348

5449

5550
def ensure_logging():
@@ -78,9 +73,6 @@ def log_to(path):
7873

7974

8075
def configure(properties=None, **kwargs):
81-
if _settrace.called:
82-
raise RuntimeError("debug adapter is already running")
83-
8476
ensure_logging()
8577
log.debug("configure{0!r}", (properties, kwargs))
8678

@@ -104,9 +96,6 @@ def configure(properties=None, **kwargs):
10496

10597
def _starts_debugging(func):
10698
def debug(address, **kwargs):
107-
if _settrace.called:
108-
raise RuntimeError("this process already has a debug adapter")
109-
11099
try:
111100
_, port = address
112101
except Exception:
@@ -116,7 +105,7 @@ def debug(address, **kwargs):
116105
port.__index__() # ensure it's int-like
117106
except Exception:
118107
raise ValueError("expected port or (host, port)")
119-
if not (0 <= port < 2 ** 16):
108+
if not (0 <= port < 2**16):
120109
raise ValueError("invalid port number")
121110

122111
ensure_logging()
@@ -150,10 +139,14 @@ def listen(address, settrace_kwargs, in_process_debug_adapter=False):
150139
# Errors below are logged with level="info", because the caller might be catching
151140
# and handling exceptions, and we don't want to spam their stderr unnecessarily.
152141

142+
if listen.called:
143+
# Multiple calls to listen() cause the debuggee to hang
144+
raise RuntimeError("debugpy.listen() has already been called on this process")
145+
153146
if in_process_debug_adapter:
154147
host, port = address
155148
log.info("Listening: pydevd without debugpy adapter: {0}:{1}", host, port)
156-
settrace_kwargs['patch_multiprocessing'] = False
149+
settrace_kwargs["patch_multiprocessing"] = False
157150
_settrace(
158151
host=host,
159152
port=port,
@@ -218,7 +211,10 @@ def listen(address, settrace_kwargs, in_process_debug_adapter=False):
218211
try:
219212
global _adapter_process
220213
_adapter_process = subprocess.Popen(
221-
adapter_args, close_fds=True, creationflags=creationflags, env=python_env
214+
adapter_args,
215+
close_fds=True,
216+
creationflags=creationflags,
217+
env=python_env,
222218
)
223219
if os.name == "posix":
224220
# It's going to fork again to daemonize, so we need to wait on it to
@@ -288,8 +284,11 @@ def listen(address, settrace_kwargs, in_process_debug_adapter=False):
288284
**settrace_kwargs
289285
)
290286
log.info("pydevd is connected to adapter at {0}:{1}", server_host, server_port)
287+
listen.called = True
291288
return client_host, client_port
292289

290+
listen.called = False
291+
293292

294293
@_starts_debugging
295294
def connect(address, settrace_kwargs, access_token=None):

tests/debugpy/test_attach.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,51 @@ def code_to_debug():
102102

103103
session.request_continue()
104104

105+
def test_multiple_listen_raises_exception(pyfile):
106+
@pyfile
107+
def code_to_debug():
108+
import debuggee
109+
import debugpy
110+
import sys
111+
112+
from debuggee import backchannel
113+
114+
debuggee.setup()
115+
_, host, port = sys.argv
116+
port = int(port)
117+
debugpy.listen(address=(host, port))
118+
try:
119+
debugpy.listen(address=(host, port))
120+
except RuntimeError:
121+
backchannel.send("listen_exception")
122+
123+
debugpy.wait_for_client()
124+
debugpy.breakpoint()
125+
print("break") # @breakpoint
126+
127+
host, port = runners.attach_connect.host, runners.attach_connect.port
128+
with debug.Session() as session:
129+
backchannel = session.open_backchannel()
130+
session.spawn_debuggee(
131+
[
132+
code_to_debug,
133+
host,
134+
port,
135+
]
136+
)
137+
138+
session.wait_for_adapter_socket()
139+
session.expect_server_socket()
140+
session.connect_to_adapter((host, port))
141+
with session.request_attach():
142+
pass
143+
144+
session.wait_for_stop(
145+
expected_frames=[some.dap.frame(code_to_debug, "breakpoint")]
146+
)
147+
assert backchannel.receive() == "listen_exception"
148+
session.request_continue()
149+
105150

106151
@pytest.mark.parametrize("run", runners.all_attach_connect)
107152
def test_reattach(pyfile, target, run):

0 commit comments

Comments
 (0)