mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
parent
2b0140e22c
commit
14982d90ba
41 changed files with 317 additions and 148 deletions
|
|
@ -45,7 +45,8 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
|
||||||
"Jinja", // Enables Jinja (Flask) Template debugging
|
"Jinja", // Enables Jinja (Flask) Template debugging
|
||||||
"FixFilePathCase", // See FIX_FILE_PATH_CASE in wrapper.py
|
"FixFilePathCase", // See FIX_FILE_PATH_CASE in wrapper.py
|
||||||
"DebugStdLib" // Whether to enable debugging of standard library functions
|
"DebugStdLib" // Whether to enable debugging of standard library functions
|
||||||
"WindowsClient" // Whether client OS is Windows or not
|
"WindowsClient" // Whether client OS is Windows
|
||||||
|
"UnixClient" // Whether client OS is Unix
|
||||||
],
|
],
|
||||||
"pathMappings": [
|
"pathMappings": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -713,7 +713,7 @@ DEBUG_OPTIONS_PARSER = {
|
||||||
'DJANGO_DEBUG': bool_parser,
|
'DJANGO_DEBUG': bool_parser,
|
||||||
'FLASK_DEBUG': bool_parser,
|
'FLASK_DEBUG': bool_parser,
|
||||||
'FIX_FILE_PATH_CASE': bool_parser,
|
'FIX_FILE_PATH_CASE': bool_parser,
|
||||||
'WINDOWS_CLIENT': bool_parser,
|
'CLIENT_OS_TYPE': unquote,
|
||||||
'DEBUG_STDLIB': bool_parser,
|
'DEBUG_STDLIB': bool_parser,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -727,7 +727,8 @@ DEBUG_OPTIONS_BY_FLAG = {
|
||||||
'Jinja': 'FLASK_DEBUG=True',
|
'Jinja': 'FLASK_DEBUG=True',
|
||||||
'FixFilePathCase': 'FIX_FILE_PATH_CASE=True',
|
'FixFilePathCase': 'FIX_FILE_PATH_CASE=True',
|
||||||
'DebugStdLib': 'DEBUG_STDLIB=True',
|
'DebugStdLib': 'DEBUG_STDLIB=True',
|
||||||
'WindowsClient': 'WINDOWS_CLIENT=True',
|
'WindowsClient': 'CLIENT_OS_TYPE=WINDOWS',
|
||||||
|
'UnixClient': 'CLIENT_OS_TYPE=UNIX',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -776,7 +777,7 @@ def _parse_debug_options(opts):
|
||||||
INTERPRETER_OPTIONS=string
|
INTERPRETER_OPTIONS=string
|
||||||
WEB_BROWSER_URL=string url
|
WEB_BROWSER_URL=string url
|
||||||
DJANGO_DEBUG=True|False
|
DJANGO_DEBUG=True|False
|
||||||
WINDOWS_CLIENT=True|False
|
CLIENT_OS_TYPE=WINDOWS|UNIX
|
||||||
DEBUG_STDLIB=True|False
|
DEBUG_STDLIB=True|False
|
||||||
"""
|
"""
|
||||||
options = {}
|
options = {}
|
||||||
|
|
@ -793,8 +794,8 @@ def _parse_debug_options(opts):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if 'WINDOWS_CLIENT' not in options:
|
if 'CLIENT_OS_TYPE' not in options:
|
||||||
options['WINDOWS_CLIENT'] = platform.system() == 'Windows' # noqa
|
options['CLIENT_OS_TYPE'] = 'WINDOWS' if platform.system() == 'Windows' else 'UNIX' # noqa
|
||||||
|
|
||||||
return options
|
return options
|
||||||
|
|
||||||
|
|
@ -1224,6 +1225,7 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
|
||||||
self.source_map = IDMap()
|
self.source_map = IDMap()
|
||||||
self.enable_source_references = False
|
self.enable_source_references = False
|
||||||
self.next_var_ref = 0
|
self.next_var_ref = 0
|
||||||
|
self._path_mappings = []
|
||||||
self.exceptions_mgr = ExceptionsManager(self)
|
self.exceptions_mgr = ExceptionsManager(self)
|
||||||
self.modules_mgr = ModulesManager(self)
|
self.modules_mgr = ModulesManager(self)
|
||||||
self.internals_filter = InternalsFilter()
|
self.internals_filter = InternalsFilter()
|
||||||
|
|
@ -1419,34 +1421,34 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _initialize_path_maps(self, args):
|
def _initialize_path_maps(self, args):
|
||||||
pathMaps = []
|
self._path_mappings = []
|
||||||
for pathMapping in args.get('pathMappings', []):
|
for pathMapping in args.get('pathMappings', []):
|
||||||
localRoot = pathMapping.get('localRoot', '')
|
localRoot = pathMapping.get('localRoot', '')
|
||||||
remoteRoot = pathMapping.get('remoteRoot', '')
|
remoteRoot = pathMapping.get('remoteRoot', '')
|
||||||
if (len(localRoot) > 0 and len(remoteRoot) > 0):
|
if (len(localRoot) > 0 and len(remoteRoot) > 0):
|
||||||
pathMaps.append((localRoot, remoteRoot))
|
self._path_mappings.append((localRoot, remoteRoot))
|
||||||
|
|
||||||
if len(pathMaps) > 0:
|
if len(self._path_mappings) > 0:
|
||||||
pydevd_file_utils.setup_client_server_paths(pathMaps)
|
pydevd_file_utils.setup_client_server_paths(self._path_mappings)
|
||||||
|
|
||||||
def _send_cmd_version_command(self):
|
def _send_cmd_version_command(self):
|
||||||
cmd = pydevd_comm.CMD_VERSION
|
cmd = pydevd_comm.CMD_VERSION
|
||||||
windows_client = self.debug_options.get(
|
default_os_type = 'WINDOWS' if platform.system() == 'Windows' else 'UNIX' # noqa
|
||||||
'WINDOWS_CLIENT',
|
client_os_type = self.debug_options.get(
|
||||||
platform.system() == 'Windows')
|
'CLIENT_OS_TYPE', default_os_type)
|
||||||
os_id = 'WINDOWS' if windows_client else 'UNIX'
|
os_id = client_os_type
|
||||||
msg = '1.1\t{}\tID'.format(os_id)
|
msg = '1.1\t{}\tID'.format(os_id)
|
||||||
return self.pydevd_request(cmd, msg)
|
return self.pydevd_request(cmd, msg)
|
||||||
|
|
||||||
@async_handler
|
@async_handler
|
||||||
def _handle_attach(self, args):
|
def _handle_attach(self, args):
|
||||||
self._initialize_path_maps(args)
|
|
||||||
yield self._send_cmd_version_command()
|
yield self._send_cmd_version_command()
|
||||||
|
self._initialize_path_maps(args)
|
||||||
|
|
||||||
@async_handler
|
@async_handler
|
||||||
def _handle_launch(self, args):
|
def _handle_launch(self, args):
|
||||||
self._initialize_path_maps(args)
|
|
||||||
yield self._send_cmd_version_command()
|
yield self._send_cmd_version_command()
|
||||||
|
self._initialize_path_maps(args)
|
||||||
|
|
||||||
def _handle_detach(self):
|
def _handle_detach(self):
|
||||||
debug('detaching')
|
debug('detaching')
|
||||||
|
|
@ -1557,34 +1559,20 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
|
||||||
if self.start_reason == 'launch':
|
if self.start_reason == 'launch':
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
# If we have no path mappings, then always enable source references.
|
||||||
|
autogen = len(self._path_mappings) == 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.source_map.to_vscode(filename, autogen=False)
|
return self.source_map.to_vscode(filename, autogen=autogen)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# If attaching to a local process (then remote and local are same)
|
pass
|
||||||
for local_prefix, remote_prefix in pydevd_file_utils.PATHS_FROM_ECLIPSE_TO_PYTHON: # noqa
|
|
||||||
if local_prefix != remote_prefix:
|
|
||||||
continue
|
|
||||||
if filename.startswith(local_prefix): # noqa
|
|
||||||
return 0
|
|
||||||
if platform.system() == 'Windows' and filename.upper().startswith(local_prefix.upper()): # noqa
|
|
||||||
return 0
|
|
||||||
|
|
||||||
client_filename = pydevd_file_utils.norm_file_to_client(filename)
|
# If file has been mapped, then source is available on client.
|
||||||
|
for local_prefix, remote_prefix in self._path_mappings:
|
||||||
|
if filename.startswith(local_prefix):
|
||||||
|
return 0
|
||||||
|
|
||||||
# If the mapped file is the same as the file we provided,
|
return self.source_map.to_vscode(filename, autogen=True)
|
||||||
# then we can generate a soure reference.
|
|
||||||
if client_filename == filename:
|
|
||||||
return 0
|
|
||||||
elif platform.system() == 'Windows' and \
|
|
||||||
client_filename.upper() == filename.upper():
|
|
||||||
return 0
|
|
||||||
elif client_filename.replace('\\', '/') == filename.replace('\\', '/'):
|
|
||||||
# If remote is Unix and local is Windows, then PyDevD will
|
|
||||||
# replace the path separator in remote with with
|
|
||||||
# the os path separator of remote client
|
|
||||||
return 0
|
|
||||||
else:
|
|
||||||
return self.source_map.to_vscode(filename, autogen=True)
|
|
||||||
|
|
||||||
@async_handler
|
@async_handler
|
||||||
def on_stackTrace(self, request, args):
|
def on_stackTrace(self, request, args):
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,13 @@ COPIED_ENV = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
ConnectionRefusedError
|
||||||
|
except Exception:
|
||||||
|
class ConnectionRefusedError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _copy_env(verbose=False, env=None):
|
def _copy_env(verbose=False, env=None):
|
||||||
variables = {k: v for k, v in os.environ.items() if k in COPIED_ENV}
|
variables = {k: v for k, v in os.environ.items() if k in COPIED_ENV}
|
||||||
# TODO: Be smarter about the seed?
|
# TODO: Be smarter about the seed?
|
||||||
|
|
|
||||||
|
|
@ -125,12 +125,20 @@ class LifecycleTests(HighlevelTest, unittest.TestCase):
|
||||||
# TODO: Ensure we see the "terminated" and "exited" events.
|
# TODO: Ensure we see the "terminated" and "exited" events.
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def test_attach_from_unix_os(self):
|
def test_attach_from_unix_os_vsc(self):
|
||||||
attach_args = {'options': 'WINDOWS_CLIENT=False'}
|
attach_args = {'debugOptions': ['UnixClient']}
|
||||||
self.attach(expected_os_id='UNIX', attach_args=attach_args)
|
self.attach(expected_os_id='UNIX', attach_args=attach_args)
|
||||||
|
|
||||||
|
def test_attach_from_unix_os(self):
|
||||||
|
attach_args = {'options': 'CLIENT_OS_TYPE=UNIX'}
|
||||||
|
self.attach(expected_os_id='UNIX', attach_args=attach_args)
|
||||||
|
|
||||||
|
def test_attach_from_win_os_vsc(self):
|
||||||
|
attach_args = {'debugOptions': ['WindowsClient']}
|
||||||
|
self.attach(expected_os_id='WINDOWS', attach_args=attach_args)
|
||||||
|
|
||||||
def test_attach_from_windows_os(self):
|
def test_attach_from_windows_os(self):
|
||||||
attach_args = {'options': 'WINDOWS_CLIENT=True'}
|
attach_args = {'options': 'CLIENT_OS_TYPE=WINDOWS'}
|
||||||
self.attach(expected_os_id='WINDOWS', attach_args=attach_args)
|
self.attach(expected_os_id='WINDOWS', attach_args=attach_args)
|
||||||
|
|
||||||
def test_launch(self):
|
def test_launch(self):
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
import mymod_bar.bar
|
|
||||||
|
|
||||||
|
|
||||||
def do_foo():
|
|
||||||
mymod_bar.bar.do_bar()
|
|
||||||
|
|
||||||
|
|
||||||
do_foo()
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import mypkg_bar.bar
|
||||||
|
|
||||||
|
|
||||||
|
def do_foo():
|
||||||
|
mypkg_bar.bar.do_bar()
|
||||||
|
|
||||||
|
|
||||||
|
do_foo()
|
||||||
11
tests/resources/system_tests/test_forever/attach_forever.py
Normal file
11
tests/resources/system_tests/test_forever/attach_forever.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import ptvsd
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
ptvsd.enable_attach((sys.argv[1], sys.argv[2]))
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
time.sleep(0.1)
|
||||||
|
print(i)
|
||||||
|
i += 1
|
||||||
|
|
@ -2,7 +2,9 @@ import contextlib
|
||||||
import os
|
import os
|
||||||
import ptvsd
|
import ptvsd
|
||||||
import signal
|
import signal
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
@ -234,24 +236,46 @@ class LifecycleTestsBase(TestsBase, unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
time.sleep(1) # wait for socket connections to die out.
|
time.sleep(1) # wait for socket connections to die out.
|
||||||
|
|
||||||
def _wrap_and_reraise(ex, session):
|
def _wrap_and_reraise(session, ex, exc_type, exc_value, exc_traceback):
|
||||||
|
"""If we have connetion errors, then re-raised wrapped in
|
||||||
|
ConnectionTimeoutError. If using py3, then chain exceptions so
|
||||||
|
we do not loose the original exception, else try hack approach
|
||||||
|
for py27."""
|
||||||
messages = []
|
messages = []
|
||||||
|
formatted_ex = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)) # noqa
|
||||||
try:
|
try:
|
||||||
messages = [str(msg) for msg in
|
messages = [str(msg) for msg in
|
||||||
_strip_newline_output_events(session.received)]
|
_strip_newline_output_events(session.received)]
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
messages = os.linesep.join(messages)
|
fmt = {
|
||||||
|
"sep": os.linesep,
|
||||||
|
"messages": os.linesep.join(messages),
|
||||||
|
"error": ''.join(traceback.format_exception_only(exc_type, exc_value)) # noqa
|
||||||
|
}
|
||||||
|
message = """
|
||||||
|
|
||||||
|
Session Messages:
|
||||||
|
-----------------
|
||||||
|
%(messages)s
|
||||||
|
|
||||||
|
Original Error:
|
||||||
|
---------------
|
||||||
|
%(error)s""" % fmt
|
||||||
|
|
||||||
try:
|
try:
|
||||||
raise Exception(messages) from ex
|
# Chain the original exception for py3.
|
||||||
except Exception:
|
exec('raise Exception(message) from ex', globals(), locals())
|
||||||
print(messages)
|
except SyntaxError:
|
||||||
raise ex
|
# This happens when using py27.
|
||||||
|
message = message + os.linesep + formatted_ex
|
||||||
|
exec("raise Exception(message)", globals(), locals())
|
||||||
|
|
||||||
def _handle_exception(ex, adapter, session):
|
def _handle_exception(ex, adapter, session):
|
||||||
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||||
_kill_proc(adapter.pid)
|
_kill_proc(adapter.pid)
|
||||||
_wrap_and_reraise(ex, session)
|
_wrap_and_reraise(session, ex, exc_type, exc_value, exc_traceback)
|
||||||
|
|
||||||
if debug_info.attachtype == 'import' and \
|
if debug_info.attachtype == 'import' and \
|
||||||
debug_info.modulename is not None:
|
debug_info.modulename is not None:
|
||||||
|
|
|
||||||
|
|
@ -111,10 +111,10 @@ class LaunchFileTests(BasicTests):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LaunchModuleTests(BasicTests):
|
class LaunchPackageTests(BasicTests):
|
||||||
|
|
||||||
def test_with_output(self):
|
def test_with_output(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
cwd = WITH_OUTPUT.root
|
cwd = WITH_OUTPUT.root
|
||||||
env = WITH_OUTPUT.env_with_py_path()
|
env = WITH_OUTPUT.env_with_py_path()
|
||||||
self.run_test_output(
|
self.run_test_output(
|
||||||
|
|
@ -122,7 +122,7 @@ class LaunchModuleTests(BasicTests):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_without_output(self):
|
def test_without_output(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
cwd = WITHOUT_OUTPUT.root
|
cwd = WITHOUT_OUTPUT.root
|
||||||
env = WITHOUT_OUTPUT.env_with_py_path()
|
env = WITHOUT_OUTPUT.env_with_py_path()
|
||||||
self.run_test_without_output(
|
self.run_test_without_output(
|
||||||
|
|
@ -131,7 +131,7 @@ class LaunchModuleTests(BasicTests):
|
||||||
|
|
||||||
@unittest.skip('Broken')
|
@unittest.skip('Broken')
|
||||||
def test_termination(self):
|
def test_termination(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
cwd = TEST_TERMINATION_FILES.root
|
cwd = TEST_TERMINATION_FILES.root
|
||||||
env = TEST_TERMINATION_FILES.env_with_py_path()
|
env = TEST_TERMINATION_FILES.env_with_py_path()
|
||||||
self.run_test_output(
|
self.run_test_output(
|
||||||
|
|
@ -143,7 +143,7 @@ class LaunchModuleTests(BasicTests):
|
||||||
|
|
||||||
@unittest.skip('Broken')
|
@unittest.skip('Broken')
|
||||||
def test_arguments(self):
|
def test_arguments(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
cwd = WITH_ARGS.root
|
cwd = WITH_ARGS.root
|
||||||
env = WITH_ARGS.env_with_py_path()
|
env = WITH_ARGS.env_with_py_path()
|
||||||
argv = ['arg1', 'arg2']
|
argv = ['arg1', 'arg2']
|
||||||
|
|
@ -213,10 +213,10 @@ class PTVSDAttachTests(BasicTests):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ServerAttachModuleTests(BasicTests):
|
class ServerAttachPackageTests(BasicTests):
|
||||||
|
|
||||||
def test_with_output(self):
|
def test_with_output(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
cwd = WITH_OUTPUT.root
|
cwd = WITH_OUTPUT.root
|
||||||
env = WITH_OUTPUT.env_with_py_path()
|
env = WITH_OUTPUT.env_with_py_path()
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
@ -231,7 +231,7 @@ class ServerAttachModuleTests(BasicTests):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_without_output(self):
|
def test_without_output(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
cwd = WITHOUT_OUTPUT.root
|
cwd = WITHOUT_OUTPUT.root
|
||||||
env = WITHOUT_OUTPUT.env_with_py_path()
|
env = WITHOUT_OUTPUT.env_with_py_path()
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
@ -246,11 +246,11 @@ class ServerAttachModuleTests(BasicTests):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PTVSDAttachModuleTests(BasicTests):
|
class PTVSDAttachPackageTests(BasicTests):
|
||||||
|
|
||||||
def test_with_output(self):
|
def test_with_output(self):
|
||||||
#self.enable_verbose()
|
#self.enable_verbose()
|
||||||
module_name = 'mymod_attach1'
|
module_name = 'mypkg_attach1'
|
||||||
cwd = WITH_OUTPUT.root
|
cwd = WITH_OUTPUT.root
|
||||||
env = WITH_OUTPUT.env_with_py_path()
|
env = WITH_OUTPUT.env_with_py_path()
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
@ -266,7 +266,7 @@ class PTVSDAttachModuleTests(BasicTests):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_without_output(self):
|
def test_without_output(self):
|
||||||
module_name = 'mymod_attach1'
|
module_name = 'mypkg_attach1'
|
||||||
cwd = WITHOUT_OUTPUT.root
|
cwd = WITHOUT_OUTPUT.root
|
||||||
env = WITHOUT_OUTPUT.env_with_py_path()
|
env = WITHOUT_OUTPUT.env_with_py_path()
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,17 @@ TEST_FILES = TestResources.from_module(__name__)
|
||||||
class BreakpointTests(LifecycleTestsBase):
|
class BreakpointTests(LifecycleTestsBase):
|
||||||
|
|
||||||
def run_test_with_break_points(self, debug_info, bp_filename, bp_line):
|
def run_test_with_break_points(self, debug_info, bp_filename, bp_line):
|
||||||
options = {'debugOptions': ['RedirectOutput']}
|
pathMappings = []
|
||||||
|
# Required to ensure sourceReference = 0
|
||||||
|
if (debug_info.starttype == 'attach'):
|
||||||
|
pathMappings.append({
|
||||||
|
'localRoot': debug_info.cwd,
|
||||||
|
'remoteRoot': debug_info.cwd
|
||||||
|
})
|
||||||
|
options = {
|
||||||
|
'debugOptions': ['RedirectOutput'],
|
||||||
|
'pathMappings': pathMappings
|
||||||
|
}
|
||||||
breakpoints = [{
|
breakpoints = [{
|
||||||
'source': {
|
'source': {
|
||||||
'path': bp_filename
|
'path': bp_filename
|
||||||
|
|
@ -82,7 +92,7 @@ class BreakpointTests(LifecycleTestsBase):
|
||||||
|
|
||||||
def run_test_with_break_points_across_files(
|
def run_test_with_break_points_across_files(
|
||||||
self, debug_info, first_file, second_file, second_file_line,
|
self, debug_info, first_file, second_file, second_file_line,
|
||||||
expected_modules, expected_stacktrace):
|
expected_stacktrace):
|
||||||
breakpoints = [{
|
breakpoints = [{
|
||||||
'source': {
|
'source': {
|
||||||
'path': second_file
|
'path': second_file
|
||||||
|
|
@ -111,14 +121,6 @@ class BreakpointTests(LifecycleTestsBase):
|
||||||
|
|
||||||
session.send_request('continue', threadId=tid)
|
session.send_request('continue', threadId=tid)
|
||||||
|
|
||||||
received = list(_strip_newline_output_events(session.received))
|
|
||||||
|
|
||||||
for mod in expected_modules:
|
|
||||||
found_mod = self.find_events(received, 'module', mod)
|
|
||||||
self.assertEqual(len(found_mod),
|
|
||||||
1,
|
|
||||||
'Modul not found {}'.format(mod))
|
|
||||||
|
|
||||||
self.assert_is_subset(stacktrace, expected_stacktrace)
|
self.assert_is_subset(stacktrace, expected_stacktrace)
|
||||||
|
|
||||||
def run_test_conditional_break_points(self, debug_info):
|
def run_test_conditional_break_points(self, debug_info):
|
||||||
|
|
@ -289,19 +291,6 @@ class LaunchFileTests(BreakpointTests):
|
||||||
first_file = TEST_FILES.resolve('foo.py')
|
first_file = TEST_FILES.resolve('foo.py')
|
||||||
second_file = TEST_FILES.resolve('bar.py')
|
second_file = TEST_FILES.resolve('bar.py')
|
||||||
cwd = os.path.dirname(first_file)
|
cwd = os.path.dirname(first_file)
|
||||||
expected_modules = [{
|
|
||||||
'reason': 'new',
|
|
||||||
'module': {
|
|
||||||
'path': second_file,
|
|
||||||
'name': 'bar'
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
'reason': 'new',
|
|
||||||
'module': {
|
|
||||||
'path': first_file,
|
|
||||||
'name': '__main__'
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
expected_stacktrace = {
|
expected_stacktrace = {
|
||||||
'stackFrames': [{
|
'stackFrames': [{
|
||||||
'name': 'do_bar',
|
'name': 'do_bar',
|
||||||
|
|
@ -335,7 +324,6 @@ class LaunchFileTests(BreakpointTests):
|
||||||
first_file,
|
first_file,
|
||||||
second_file,
|
second_file,
|
||||||
2,
|
2,
|
||||||
expected_modules,
|
|
||||||
expected_stacktrace,
|
expected_stacktrace,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -422,10 +410,10 @@ class LaunchFileTests(BreakpointTests):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LaunchModuleTests(BreakpointTests):
|
class LaunchPackageTests(BreakpointTests):
|
||||||
|
|
||||||
def test_with_break_points(self):
|
def test_with_break_points(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.root
|
cwd = TEST_FILES.root
|
||||||
bp_filename = os.path.join(cwd, module_name, '__init__.py')
|
bp_filename = os.path.join(cwd, module_name, '__init__.py')
|
||||||
|
|
@ -436,25 +424,11 @@ class LaunchModuleTests(BreakpointTests):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_with_break_points_across_files(self):
|
def test_with_break_points_across_files(self):
|
||||||
module_name = 'mymod_foo'
|
module_name = 'mypkg_foo'
|
||||||
first_file = TEST_FILES.resolve(module_name, '__init__.py')
|
first_file = TEST_FILES.resolve(module_name, '__init__.py')
|
||||||
second_file = TEST_FILES.resolve('mymod_bar', 'bar.py')
|
second_file = TEST_FILES.resolve('mypkg_bar', 'bar.py')
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.root
|
cwd = TEST_FILES.root
|
||||||
expected_modules = [{
|
|
||||||
'reason': 'new',
|
|
||||||
'module': {
|
|
||||||
'package': 'mymod_bar',
|
|
||||||
'path': second_file,
|
|
||||||
'name': 'mymod_bar.bar'
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
'reason': 'new',
|
|
||||||
'module': {
|
|
||||||
'path': first_file,
|
|
||||||
'name': '__main__'
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
expected_stacktrace = {
|
expected_stacktrace = {
|
||||||
'stackFrames': [{
|
'stackFrames': [{
|
||||||
'name': 'do_bar',
|
'name': 'do_bar',
|
||||||
|
|
@ -488,7 +462,6 @@ class LaunchModuleTests(BreakpointTests):
|
||||||
first_file,
|
first_file,
|
||||||
second_file,
|
second_file,
|
||||||
2,
|
2,
|
||||||
expected_modules,
|
|
||||||
expected_stacktrace,
|
expected_stacktrace,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -530,10 +503,10 @@ class PTVSDAttachTests(BreakpointTests):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ServerAttachModuleTests(BreakpointTests):
|
class ServerAttachPackageTests(BreakpointTests):
|
||||||
|
|
||||||
def test_with_break_points(self):
|
def test_with_break_points(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.root
|
cwd = TEST_FILES.root
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
@ -552,10 +525,10 @@ class ServerAttachModuleTests(BreakpointTests):
|
||||||
|
|
||||||
|
|
||||||
@unittest.skip('Needs fixing')
|
@unittest.skip('Needs fixing')
|
||||||
class PTVSDAttachModuleTests(BreakpointTests):
|
class PTVSDAttachPackageTests(BreakpointTests):
|
||||||
|
|
||||||
def test_with_break_points(self):
|
def test_with_break_points(self):
|
||||||
module_name = 'mymod_attach1'
|
module_name = 'mypkg_attach1'
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.root
|
cwd = TEST_FILES.root
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
|
||||||
|
|
@ -100,14 +100,14 @@ class LaunchFileTests(ExceptionTests):
|
||||||
class LaunchModuleExceptionLifecycleTests(ExceptionTests):
|
class LaunchModuleExceptionLifecycleTests(ExceptionTests):
|
||||||
|
|
||||||
def test_breaking_into_handled_exceptions(self):
|
def test_breaking_into_handled_exceptions(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.parent.root
|
cwd = TEST_FILES.parent.root
|
||||||
self.run_test_breaking_into_handled_exceptions(
|
self.run_test_breaking_into_handled_exceptions(
|
||||||
DebugInfo(modulename=module_name, env=env, cwd=cwd))
|
DebugInfo(modulename=module_name, env=env, cwd=cwd))
|
||||||
|
|
||||||
def test_not_breaking_into_handled_exceptions(self):
|
def test_not_breaking_into_handled_exceptions(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.parent.root
|
cwd = TEST_FILES.parent.root
|
||||||
self.run_test_not_breaking_into_handled_exceptions(
|
self.run_test_not_breaking_into_handled_exceptions(
|
||||||
|
|
@ -174,7 +174,7 @@ class PTVSDAttachExceptionLifecycleTests(ExceptionTests):
|
||||||
class ServerAttachModuleExceptionLifecycleTests(ExceptionTests):
|
class ServerAttachModuleExceptionLifecycleTests(ExceptionTests):
|
||||||
|
|
||||||
def test_breaking_into_handled_exceptions(self):
|
def test_breaking_into_handled_exceptions(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.root
|
cwd = TEST_FILES.root
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
@ -188,7 +188,7 @@ class ServerAttachModuleExceptionLifecycleTests(ExceptionTests):
|
||||||
))
|
))
|
||||||
|
|
||||||
def test_not_breaking_into_handled_exceptions(self):
|
def test_not_breaking_into_handled_exceptions(self):
|
||||||
module_name = 'mymod_launch1'
|
module_name = 'mypkg_launch1'
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.root
|
cwd = TEST_FILES.root
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
@ -206,7 +206,7 @@ class ServerAttachModuleExceptionLifecycleTests(ExceptionTests):
|
||||||
class PTVSDAttachModuleExceptionLifecycleTests(ExceptionTests):
|
class PTVSDAttachModuleExceptionLifecycleTests(ExceptionTests):
|
||||||
|
|
||||||
def test_breaking_into_handled_exceptions(self):
|
def test_breaking_into_handled_exceptions(self):
|
||||||
module_name = 'mymod_attach1'
|
module_name = 'mypkg_attach1'
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.root
|
cwd = TEST_FILES.root
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
@ -221,7 +221,7 @@ class PTVSDAttachModuleExceptionLifecycleTests(ExceptionTests):
|
||||||
))
|
))
|
||||||
|
|
||||||
def test_not_breaking_into_handled_exceptions(self):
|
def test_not_breaking_into_handled_exceptions(self):
|
||||||
module_name = 'mymod_attach1'
|
module_name = 'mypkg_attach1'
|
||||||
env = TEST_FILES.env_with_py_path()
|
env = TEST_FILES.env_with_py_path()
|
||||||
cwd = TEST_FILES.root
|
cwd = TEST_FILES.root
|
||||||
argv = ['localhost', str(PORT)]
|
argv = ['localhost', str(PORT)]
|
||||||
|
|
|
||||||
|
|
@ -474,29 +474,6 @@ class LifecycleTests(LifecycleTestsBase):
|
||||||
'name': 'MainThread',
|
'name': 'MainThread',
|
||||||
}],
|
}],
|
||||||
}),
|
}),
|
||||||
self.new_event(
|
|
||||||
'module',
|
|
||||||
module={
|
|
||||||
'id': 1,
|
|
||||||
'name': '__main__',
|
|
||||||
'path': filename,
|
|
||||||
'package': None,
|
|
||||||
},
|
|
||||||
reason='new',
|
|
||||||
),
|
|
||||||
self.new_response(req_stacktrace1.req, **{
|
|
||||||
'totalFrames': 1,
|
|
||||||
'stackFrames': [{
|
|
||||||
'id': 1,
|
|
||||||
'name': '<module>',
|
|
||||||
'source': {
|
|
||||||
'path': filename,
|
|
||||||
'sourceReference': 0,
|
|
||||||
},
|
|
||||||
'line': bp1,
|
|
||||||
'column': 1,
|
|
||||||
}],
|
|
||||||
}),
|
|
||||||
self.new_response(req_disconnect.req),
|
self.new_response(req_disconnect.req),
|
||||||
])
|
])
|
||||||
self.messages.reset_all()
|
self.messages.reset_all()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from tests.helpers.debugsession import Awaitable
|
||||||
from tests.helpers.resource import TestResources
|
from tests.helpers.resource import TestResources
|
||||||
from tests.helpers.socket import resolve_hostname
|
from tests.helpers.socket import resolve_hostname
|
||||||
from . import (
|
from . import (
|
||||||
|
|
@ -11,9 +15,20 @@ from . import (
|
||||||
|
|
||||||
TEST_FILES = TestResources.from_module('tests.system_tests.test_basic')
|
TEST_FILES = TestResources.from_module('tests.system_tests.test_basic')
|
||||||
WITH_OUTPUT = TEST_FILES.sub('test_output')
|
WITH_OUTPUT = TEST_FILES.sub('test_output')
|
||||||
|
SYSTEM_TEST_FILES = TestResources.from_module('tests.system_tests')
|
||||||
|
WITH_TEST_FORVER = SYSTEM_TEST_FILES.sub('test_forever')
|
||||||
|
|
||||||
|
|
||||||
class RemoteTests(LifecycleTestsBase):
|
class RemoteTests(LifecycleTestsBase):
|
||||||
|
def _assert_stacktrace_is_subset(self, stacktrace, expected_stacktrace):
|
||||||
|
# Ignore path case on Windows.
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
for frame in stacktrace.get('stackFrames'):
|
||||||
|
frame['source']['path'] = frame['source'].get('path', '').upper() # noqa
|
||||||
|
for frame in expected_stacktrace.get('stackFrames'):
|
||||||
|
frame['source']['path'] = frame['source'].get('path', '').upper() # noqa
|
||||||
|
|
||||||
|
self.assert_is_subset(stacktrace, expected_stacktrace)
|
||||||
|
|
||||||
def run_test_attach(self, debug_info):
|
def run_test_attach(self, debug_info):
|
||||||
options = {'debugOptions': ['RedirectOutput']}
|
options = {'debugOptions': ['RedirectOutput']}
|
||||||
|
|
@ -28,6 +43,48 @@ class RemoteTests(LifecycleTestsBase):
|
||||||
self.new_event('output', category='stderr', output='no'),
|
self.new_event('output', category='stderr', output='no'),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def run_test_source_references(self,
|
||||||
|
debug_info,
|
||||||
|
expected_stacktrace,
|
||||||
|
path_mappings=[],
|
||||||
|
debug_options=[]):
|
||||||
|
options = {
|
||||||
|
'debugOptions': debug_options,
|
||||||
|
'pathMappings': path_mappings
|
||||||
|
}
|
||||||
|
|
||||||
|
with self.start_debugging(debug_info) as dbg:
|
||||||
|
(_, req_attach, _, _, _, req_threads) = lifecycle_handshake(
|
||||||
|
dbg.session,
|
||||||
|
debug_info.starttype,
|
||||||
|
options=options,
|
||||||
|
threads=True)
|
||||||
|
|
||||||
|
# wait till we enter the for loop.
|
||||||
|
time.sleep(1)
|
||||||
|
Awaitable.wait_all(req_attach, req_threads)
|
||||||
|
with dbg.session.wait_for_event('stopped') as result:
|
||||||
|
arguments = {
|
||||||
|
'source': {
|
||||||
|
'name': os.path.basename(debug_info.filename),
|
||||||
|
'path': debug_info.filename
|
||||||
|
},
|
||||||
|
'lines': [9],
|
||||||
|
'breakpoints': [{'line': 9}]
|
||||||
|
}
|
||||||
|
dbg.session.send_request('setBreakpoints', **arguments)
|
||||||
|
|
||||||
|
tid = result['msg'].body['threadId']
|
||||||
|
stacktrace = dbg.session.send_request('stackTrace', threadId=tid)
|
||||||
|
stacktrace.wait()
|
||||||
|
dbg.session.send_request('continue', threadId=tid).wait()
|
||||||
|
|
||||||
|
# Kill remove program.
|
||||||
|
os.kill(dbg.adapter.pid, signal.SIGTERM)
|
||||||
|
|
||||||
|
self._assert_stacktrace_is_subset(stacktrace.resp.body,
|
||||||
|
expected_stacktrace)
|
||||||
|
|
||||||
|
|
||||||
class AttachFileTests(RemoteTests):
|
class AttachFileTests(RemoteTests):
|
||||||
|
|
||||||
|
|
@ -86,6 +143,129 @@ class AttachFileTests(RemoteTests):
|
||||||
host=ip,
|
host=ip,
|
||||||
cwd=cwd,
|
cwd=cwd,
|
||||||
starttype='attach',
|
starttype='attach',
|
||||||
argv=argv,
|
argv=argv))
|
||||||
),
|
|
||||||
)
|
def test_source_references_should_be_returned_without_path_mappings(self):
|
||||||
|
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||||
|
cwd = os.path.dirname(filename)
|
||||||
|
argv = ['localhost', str(PORT)]
|
||||||
|
expected_stacktrace = {
|
||||||
|
'stackFrames': [{
|
||||||
|
'source': {
|
||||||
|
'path': filename,
|
||||||
|
'sourceReference': 1
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
self.run_test_source_references(
|
||||||
|
DebugInfo(
|
||||||
|
filename=filename,
|
||||||
|
attachtype='import',
|
||||||
|
cwd=cwd,
|
||||||
|
starttype='attach',
|
||||||
|
argv=argv), expected_stacktrace)
|
||||||
|
|
||||||
|
def test_source_references_should_not_be_returned_with_path_mappings(self):
|
||||||
|
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||||
|
cwd = os.path.dirname(filename)
|
||||||
|
argv = ['localhost', str(PORT)]
|
||||||
|
path_mappings = [{
|
||||||
|
'localRoot': os.path.dirname(filename),
|
||||||
|
'remoteRoot': os.path.dirname(filename)
|
||||||
|
}]
|
||||||
|
expected_stacktrace = {
|
||||||
|
'stackFrames': [{
|
||||||
|
'source': {
|
||||||
|
'path': filename,
|
||||||
|
'sourceReference': 0
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
self.run_test_source_references(
|
||||||
|
DebugInfo(
|
||||||
|
filename=filename,
|
||||||
|
attachtype='import',
|
||||||
|
cwd=cwd,
|
||||||
|
starttype='attach',
|
||||||
|
argv=argv), expected_stacktrace, path_mappings)
|
||||||
|
|
||||||
|
def test_source_references_should_be_returned_with_invalid_path_mappings(
|
||||||
|
self):
|
||||||
|
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||||
|
cwd = os.path.dirname(filename)
|
||||||
|
argv = ['localhost', str(PORT)]
|
||||||
|
path_mappings = [{
|
||||||
|
'localRoot': os.path.dirname(__file__),
|
||||||
|
'remoteRoot': os.path.dirname(__file__)
|
||||||
|
}]
|
||||||
|
expected_stacktrace = {
|
||||||
|
'stackFrames': [{
|
||||||
|
'source': {
|
||||||
|
'path': filename,
|
||||||
|
'sourceReference': 1
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
self.run_test_source_references(
|
||||||
|
DebugInfo(
|
||||||
|
filename=filename,
|
||||||
|
attachtype='import',
|
||||||
|
cwd=cwd,
|
||||||
|
starttype='attach',
|
||||||
|
argv=argv), expected_stacktrace, path_mappings)
|
||||||
|
|
||||||
|
def test_source_references_should_be_returned_with_win_client(self):
|
||||||
|
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||||
|
cwd = os.path.dirname(filename)
|
||||||
|
argv = ['localhost', str(PORT)]
|
||||||
|
client_dir = 'C:\\Development\\Projects\\src\\sub dir'
|
||||||
|
path_mappings = [{
|
||||||
|
'localRoot': client_dir,
|
||||||
|
'remoteRoot': os.path.dirname(filename)
|
||||||
|
}]
|
||||||
|
expected_stacktrace = {
|
||||||
|
'stackFrames': [{
|
||||||
|
'source': {
|
||||||
|
'path': client_dir + '\\' + os.path.basename(filename),
|
||||||
|
'sourceReference': 0
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
self.run_test_source_references(
|
||||||
|
DebugInfo(
|
||||||
|
filename=filename,
|
||||||
|
attachtype='import',
|
||||||
|
cwd=cwd,
|
||||||
|
starttype='attach',
|
||||||
|
argv=argv),
|
||||||
|
expected_stacktrace,
|
||||||
|
path_mappings=path_mappings,
|
||||||
|
debug_options=['WindowsClient'])
|
||||||
|
|
||||||
|
def test_source_references_should_be_returned_with_unix_client(self):
|
||||||
|
filename = WITH_TEST_FORVER.resolve('attach_forever.py')
|
||||||
|
cwd = os.path.dirname(filename)
|
||||||
|
argv = ['localhost', str(PORT)]
|
||||||
|
client_dir = '/Users/PeterSmith/projects/src/sub dir'
|
||||||
|
path_mappings = [{
|
||||||
|
'localRoot': client_dir,
|
||||||
|
'remoteRoot': os.path.dirname(filename)
|
||||||
|
}]
|
||||||
|
expected_stacktrace = {
|
||||||
|
'stackFrames': [{
|
||||||
|
'source': {
|
||||||
|
'path': client_dir + '/' + os.path.basename(filename),
|
||||||
|
'sourceReference': 0
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
self.run_test_source_references(
|
||||||
|
DebugInfo(
|
||||||
|
filename=filename,
|
||||||
|
attachtype='import',
|
||||||
|
cwd=cwd,
|
||||||
|
starttype='attach',
|
||||||
|
argv=argv),
|
||||||
|
expected_stacktrace,
|
||||||
|
path_mappings=path_mappings,
|
||||||
|
debug_options=['UnixClient'])
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue