Skip to content

Commit

Permalink
Show user traceback for errors raised in the debug console. Fixes #328
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Jul 24, 2020
1 parent 499bbb2 commit ad3ade8
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 2 deletions.
29 changes: 27 additions & 2 deletions src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
from _pydevd_bundle.pydevd_constants import ForkSafeLock, NULL
from _pydevd_bundle.pydevd_daemon_thread import PyDBDaemonThread
from _pydevd_bundle.pydevd_thread_lifecycle import pydevd_find_thread_by_id, resume_threads
from _pydevd_bundle.pydevd_dont_trace_files import PYDEV_FILE
try:
from urllib import quote_plus, unquote_plus # @UnresolvedImport
except:
Expand Down Expand Up @@ -953,8 +954,32 @@ def __create_frame():
if try_exec:
try:
pydevd_vars.evaluate_expression(py_db, frame, expression, is_exec=True)
except (Exception, KeyboardInterrupt) as ex:
err = ''.join(traceback.format_exception_only(type(ex), ex))
except (Exception, KeyboardInterrupt):
try:
exc, exc_type, initial_tb = sys.exc_info()
tb = initial_tb

# Show the traceback without pydevd frames.
temp_tb = tb
while temp_tb:
if py_db.get_file_type(temp_tb.tb_frame) == PYDEV_FILE:
tb = temp_tb.tb_next
temp_tb = temp_tb.tb_next

if tb is None:
tb = initial_tb
err = ''.join(traceback.format_exception(exc, exc_type, tb))

# Make sure we don't keep references to them.
exc = None
exc_type = None
tb = None
temp_tb = None
initial_tb = None
except:
err = '<Internal error - unable to get traceback when evaluating expression>'
pydev_log.exception(err)

# Currently there is an issue in VSC where returning success=false for an
# eval request, in repl context, VSC does not show the error response in
# the debug console. So return the error message in result as well.
Expand Down
45 changes: 45 additions & 0 deletions src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -2600,6 +2600,51 @@ def test_evaluate_failures(case_setup):
writer.finished_ok = True


def test_evaluate_exception_trace(case_setup, pyfile):

@pyfile
def exception_trace_file():

class A(object):

def __init__(self, a):
pass

def method():
A()

def method2():
method()

def method3():
method2()

print('TEST SUCEEDED') # Break here

with case_setup.test_file(exception_trace_file) as writer:
json_facade = JsonFacade(writer)
json_facade.write_launch(justMyCode=False)

json_facade.write_set_breakpoints(writer.get_line_index_with_content('Break here'))
json_facade.write_make_initial_run()

json_hit = json_facade.wait_for_thread_stopped()

exec_response = json_facade.evaluate('method3()', json_hit.frame_id, 'repl', success=False)
assert 'pydevd' not in exec_response.message # i.e.: don't show pydevd in the trace
assert 'method3' in exec_response.message
assert 'method2' in exec_response.message

exec_response = json_facade.evaluate('method2()', json_hit.frame_id, 'repl', success=False)
assert 'pydevd' not in exec_response.message
assert 'method3' not in exec_response.message
assert 'method2' in exec_response.message

json_facade.write_continue()

writer.finished_ok = True


@pytest.mark.parametrize('max_frames', ['default', 'all', 10]) # -1 = default, 0 = all, 10 = 10 frames
def test_exception_details(case_setup, max_frames):
with case_setup.test_file('_debugger_case_large_exception_stack.py') as writer:
Expand Down

0 comments on commit ad3ade8

Please sign in to comment.