mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
enable_attach() no longer hangs if called from top level of an imported module on Python 2.7. Fixes #1788
This commit is contained in:
parent
5fcc3e4cd5
commit
bb5da4e19f
6 changed files with 78 additions and 55 deletions
|
|
@ -115,6 +115,9 @@ except:
|
|||
# CMD_XXX constants imported for backward compatibility
|
||||
from _pydevd_bundle.pydevd_comm_constants import * # @UnusedWildImport
|
||||
|
||||
if IS_WINDOWS:
|
||||
from socket import SO_EXCLUSIVEADDRUSE
|
||||
|
||||
if IS_JYTHON:
|
||||
import org.python.core as JyCore # @UnresolvedImport
|
||||
|
||||
|
|
@ -187,10 +190,8 @@ def run_as_pydevd_daemon_thread(func, *args, **kwargs):
|
|||
class ReaderThread(PyDBDaemonThread):
|
||||
''' reader thread reads and dispatches commands in an infinite loop '''
|
||||
|
||||
def __init__(self, sock, py_db, terminate_on_socket_close=True):
|
||||
def __init__(self, sock, py_db, PyDevJsonCommandProcessor, process_net_command, terminate_on_socket_close=True):
|
||||
assert sock is not None
|
||||
from _pydevd_bundle.pydevd_process_net_command_json import PyDevJsonCommandProcessor
|
||||
from _pydevd_bundle.pydevd_process_net_command import process_net_command
|
||||
PyDBDaemonThread.__init__(self)
|
||||
self._terminate_on_socket_close = terminate_on_socket_close
|
||||
|
||||
|
|
@ -429,7 +430,6 @@ def create_server_socket(host, port):
|
|||
try:
|
||||
server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||
if IS_WINDOWS:
|
||||
from socket import SO_EXCLUSIVEADDRUSE
|
||||
server.setsockopt(SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1)
|
||||
else:
|
||||
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ except NameError:
|
|||
|
||||
import sys # Note: the sys import must be here anyways (others depend on it)
|
||||
|
||||
# Preload codecs to avoid imports to them later on which can potentially halt the debugger.
|
||||
import codecs as _codecs
|
||||
for _codec in ["ascii", "utf8", "utf-8", "latin1", "latin-1", "idna"]:
|
||||
_codecs.lookup(_codec)
|
||||
|
||||
|
||||
class DebugInfoHolder:
|
||||
# we have to put it here because it can be set through the command line (so, the
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ from _pydevd_bundle.pydevd_comm import(InternalConsoleExec,
|
|||
start_client, start_server, InternalGetBreakpointException, InternalSendCurrExceptionTrace,
|
||||
InternalSendCurrExceptionTraceProceeded, run_as_pydevd_daemon_thread)
|
||||
|
||||
from _pydevd_bundle.pydevd_process_net_command_json import PyDevJsonCommandProcessor
|
||||
from _pydevd_bundle.pydevd_process_net_command import process_net_command
|
||||
|
||||
from _pydevd_bundle.pydevd_breakpoints import stop_on_unhandled_exception
|
||||
from _pydevd_bundle.pydevd_collect_try_except_info import collect_try_except_info
|
||||
from _pydevd_bundle.pydevd_suspended_frames import SuspendedFramesManager
|
||||
|
|
@ -1127,7 +1130,13 @@ class PyDB(object):
|
|||
curr_writer.do_kill_pydev_thread()
|
||||
|
||||
self.writer = WriterThread(sock, self, terminate_on_socket_close=terminate_on_socket_close)
|
||||
self.reader = ReaderThread(sock, self, terminate_on_socket_close=terminate_on_socket_close)
|
||||
self.reader = ReaderThread(
|
||||
sock,
|
||||
self,
|
||||
PyDevJsonCommandProcessor=PyDevJsonCommandProcessor,
|
||||
process_net_command=process_net_command,
|
||||
terminate_on_socket_close=terminate_on_socket_close
|
||||
)
|
||||
self.writer.start()
|
||||
self.reader.start()
|
||||
|
||||
|
|
@ -2568,7 +2577,13 @@ class DispatchReader(ReaderThread):
|
|||
|
||||
def __init__(self, dispatcher):
|
||||
self.dispatcher = dispatcher
|
||||
ReaderThread.__init__(self, self.dispatcher.client)
|
||||
|
||||
ReaderThread.__init__(
|
||||
self,
|
||||
self.dispatcher.client,
|
||||
PyDevJsonCommandProcessor=PyDevJsonCommandProcessor,
|
||||
process_net_command=process_net_command,
|
||||
)
|
||||
|
||||
@overrides(ReaderThread._on_run)
|
||||
def _on_run(self):
|
||||
|
|
|
|||
|
|
@ -1,50 +1,3 @@
|
|||
if __name__ == '__main__':
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
port = int(sys.argv[1])
|
||||
root_dirname = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
if root_dirname not in sys.path:
|
||||
sys.path.append(root_dirname)
|
||||
|
||||
import pydevd
|
||||
try:
|
||||
pydevd._wait_for_attach() # Cannot be called before _enable_attach.
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('Expected _wait_for_attach to raise exception.')
|
||||
|
||||
assert sys.gettrace() is None
|
||||
print('enable attach to port: %s' % (port,))
|
||||
pydevd._enable_attach(('127.0.0.1', port))
|
||||
pydevd._enable_attach(('127.0.0.1', port)) # no-op in practice
|
||||
|
||||
try:
|
||||
pydevd._enable_attach(('127.0.0.1', port + 15)) # different port: raise error.
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('Expected _enable_attach to raise exception (because it is already hearing in another port).')
|
||||
|
||||
assert pydevd.get_global_debugger() is not None
|
||||
assert sys.gettrace() is not None
|
||||
|
||||
a = 10 # Break 1
|
||||
print('wait for attach')
|
||||
pydevd._wait_for_attach()
|
||||
print('finished wait for attach')
|
||||
pydevd._wait_for_attach() # Should promptly return (already connected).
|
||||
|
||||
a = 20 # Break 2
|
||||
|
||||
pydevd._wait_for_attach() # As we disconnected on the 2nd break, this one should wait until a new configurationDone.
|
||||
|
||||
a = 20 # Break 3
|
||||
|
||||
while a == 20: # Pause 1
|
||||
# The debugger should disconnect/reconnect, pause and then change 'a' to another value.
|
||||
time.sleep(1 / 20.) # Pause 2
|
||||
|
||||
print('TEST SUCEEDED!')
|
||||
# We want to call _enable_attach inside an import to make sure that it works properly that way.
|
||||
import _debugger_case_wait_for_attach_impl
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
import os
|
||||
import sys
|
||||
import time
|
||||
port = int(sys.argv[1])
|
||||
root_dirname = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
if root_dirname not in sys.path:
|
||||
sys.path.append(root_dirname)
|
||||
|
||||
import pydevd
|
||||
try:
|
||||
pydevd._wait_for_attach() # Cannot be called before _enable_attach.
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('Expected _wait_for_attach to raise exception.')
|
||||
|
||||
assert sys.gettrace() is None
|
||||
print('enable attach to port: %s' % (port,))
|
||||
pydevd._enable_attach(('127.0.0.1', port))
|
||||
pydevd._enable_attach(('127.0.0.1', port)) # no-op in practice
|
||||
|
||||
try:
|
||||
pydevd._enable_attach(('127.0.0.1', port + 15)) # different port: raise error.
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('Expected _enable_attach to raise exception (because it is already hearing in another port).')
|
||||
|
||||
assert pydevd.get_global_debugger() is not None
|
||||
assert sys.gettrace() is not None
|
||||
|
||||
a = 10 # Break 1
|
||||
print('wait for attach')
|
||||
pydevd._wait_for_attach()
|
||||
print('finished wait for attach')
|
||||
pydevd._wait_for_attach() # Should promptly return (already connected).
|
||||
|
||||
a = 20 # Break 2
|
||||
|
||||
pydevd._wait_for_attach() # As we disconnected on the 2nd break, this one should wait until a new configurationDone.
|
||||
|
||||
a = 20 # Break 3
|
||||
|
||||
while a == 20: # Pause 1
|
||||
# The debugger should disconnect/reconnect, pause and then change 'a' to another value.
|
||||
time.sleep(1 / 20.) # Pause 2
|
||||
|
||||
print('TEST SUCEEDED!')
|
||||
|
|
@ -2426,6 +2426,7 @@ def test_wait_for_attach(case_setup_remote_attach_to):
|
|||
assert next(iter(process_events)).body.startMethod == start_method
|
||||
|
||||
with case_setup_remote_attach_to.test_file('_debugger_case_wait_for_attach.py', host_port[1]) as writer:
|
||||
writer.TEST_FILE = debugger_unittest._get_debugger_test_file('_debugger_case_wait_for_attach_impl.py')
|
||||
time.sleep(.5) # Give some time for it to pass the first breakpoint and wait in 'wait_for_attach'.
|
||||
writer.start_socket_client(*host_port)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue