Skip to content

Commit

Permalink
Properly update pydevd._settrace.called (#1751)
Browse files Browse the repository at this point in the history
* Properly update pydevd._settrace.called

* Change _settrace.called to _listen.called

* Remove unnecessary underscore from listen

* Fix lint issues

* Update multiple listen test
  • Loading branch information
lukejriddle authored Dec 6, 2024
1 parent 43f4102 commit 5014f25
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 15 deletions.
29 changes: 14 additions & 15 deletions src/debugpy/server/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,9 @@ def _settrace(*args, **kwargs):
# The stdin in notification is not acted upon in debugpy, so, disable it.
kwargs.setdefault("notify_stdin", False)
try:
return pydevd.settrace(*args, **kwargs)
pydevd.settrace(*args, **kwargs)
except Exception:
raise
else:
_settrace.called = True


_settrace.called = False


def ensure_logging():
Expand Down Expand Up @@ -78,9 +73,6 @@ def log_to(path):


def configure(properties=None, **kwargs):
if _settrace.called:
raise RuntimeError("debug adapter is already running")

ensure_logging()
log.debug("configure{0!r}", (properties, kwargs))

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

def _starts_debugging(func):
def debug(address, **kwargs):
if _settrace.called:
raise RuntimeError("this process already has a debug adapter")

try:
_, port = address
except Exception:
Expand All @@ -116,7 +105,7 @@ def debug(address, **kwargs):
port.__index__() # ensure it's int-like
except Exception:
raise ValueError("expected port or (host, port)")
if not (0 <= port < 2 ** 16):
if not (0 <= port < 2**16):
raise ValueError("invalid port number")

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

if listen.called:
# Multiple calls to listen() cause the debuggee to hang
raise RuntimeError("debugpy.listen() has already been called on this process")

if in_process_debug_adapter:
host, port = address
log.info("Listening: pydevd without debugpy adapter: {0}:{1}", host, port)
settrace_kwargs['patch_multiprocessing'] = False
settrace_kwargs["patch_multiprocessing"] = False
_settrace(
host=host,
port=port,
Expand Down Expand Up @@ -218,7 +211,10 @@ def listen(address, settrace_kwargs, in_process_debug_adapter=False):
try:
global _adapter_process
_adapter_process = subprocess.Popen(
adapter_args, close_fds=True, creationflags=creationflags, env=python_env
adapter_args,
close_fds=True,
creationflags=creationflags,
env=python_env,
)
if os.name == "posix":
# It's going to fork again to daemonize, so we need to wait on it to
Expand Down Expand Up @@ -288,8 +284,11 @@ def listen(address, settrace_kwargs, in_process_debug_adapter=False):
**settrace_kwargs
)
log.info("pydevd is connected to adapter at {0}:{1}", server_host, server_port)
listen.called = True
return client_host, client_port

listen.called = False


@_starts_debugging
def connect(address, settrace_kwargs, access_token=None):
Expand Down
45 changes: 45 additions & 0 deletions tests/debugpy/test_attach.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,51 @@ def code_to_debug():

session.request_continue()

def test_multiple_listen_raises_exception(pyfile):
@pyfile
def code_to_debug():
import debuggee
import debugpy
import sys

from debuggee import backchannel

debuggee.setup()
_, host, port = sys.argv
port = int(port)
debugpy.listen(address=(host, port))
try:
debugpy.listen(address=(host, port))
except RuntimeError:
backchannel.send("listen_exception")

debugpy.wait_for_client()
debugpy.breakpoint()
print("break") # @breakpoint

host, port = runners.attach_connect.host, runners.attach_connect.port
with debug.Session() as session:
backchannel = session.open_backchannel()
session.spawn_debuggee(
[
code_to_debug,
host,
port,
]
)

session.wait_for_adapter_socket()
session.expect_server_socket()
session.connect_to_adapter((host, port))
with session.request_attach():
pass

session.wait_for_stop(
expected_frames=[some.dap.frame(code_to_debug, "breakpoint")]
)
assert backchannel.receive() == "listen_exception"
session.request_continue()


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

0 comments on commit 5014f25

Please sign in to comment.