Skip to content

Commit

Permalink
Merge pull request #5125 from ccordoba12/first-breakpoint
Browse files Browse the repository at this point in the history
PR: Correctly move to first breakpoint after entering debugging
  • Loading branch information
ccordoba12 authored Sep 5, 2017
2 parents e84c126 + 158efe8 commit 7ce6111
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 37 deletions.
57 changes: 56 additions & 1 deletion spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,62 @@ def test_calltip(main_window, qtbot):
main_window.editor.close_file()


@flaky(max_runs=3)
@pytest.mark.skipif(os.name == 'nt', reason="It times out sometimes on Windows")
def test_move_to_first_breakpoint(main_window, qtbot):
"""Test that we move to the first breakpoint if there's one present."""
# Wait until the window is fully up
shell = main_window.ipyconsole.get_current_shellwidget()
qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)

# Main variables
control = shell._control
debug_action = main_window.debug_toolbar_actions[0]
debug_button = main_window.debug_toolbar.widgetForAction(debug_action)

# Clear all breakpoints
main_window.editor.clear_all_breakpoints()

# Load test file
test_file = osp.join(LOCATION, 'script.py')
main_window.editor.load(test_file)
code_editor = main_window.editor.get_focus_widget()

# Set breakpoint
code_editor.add_remove_breakpoint(line_number=10)
qtbot.wait(500)

# Click the debug button
qtbot.mouseClick(debug_button, Qt.LeftButton)
qtbot.wait(1000)

# Verify that we are at first breakpoint
shell.clear_console()
qtbot.wait(500)
shell.kernel_client.input("list")
qtbot.wait(500)
assert "1--> 10 arr = np.array(li)" in control.toPlainText()

# Exit debugging
shell.kernel_client.input("exit")
qtbot.wait(500)

# Set breakpoint on first line with code
code_editor.add_remove_breakpoint(line_number=2)
qtbot.wait(500)

# Click the debug button
qtbot.mouseClick(debug_button, Qt.LeftButton)
qtbot.wait(1000)

# Verify that we are still on debugging
assert shell._reading

# Remove breakpoint and close test file
main_window.editor.clear_all_breakpoints()
main_window.editor.close_file()


@flaky(max_runs=3)
@pytest.mark.skipif(os.environ.get('CI', None) is None,
reason="It's not meant to be run locally")
Expand Down Expand Up @@ -844,7 +900,6 @@ def test_stop_dbg(main_window, qtbot):

# Wait until the window is fully up
shell = main_window.ipyconsole.get_current_shellwidget()
control = shell._control
qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)

# Clear all breakpoints
Expand Down
10 changes: 0 additions & 10 deletions spyder/plugins/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2401,16 +2401,6 @@ def set_dialog_size(self, size):
def debug_file(self):
"""Debug current script"""
self.run_file(debug=True)
# Fixes 2034
editor = self.get_current_editor()
breakpoints = editor.get_breakpoints()
if breakpoints:
time.sleep(0.5)
if editor.get_cursor_line_number() == breakpoints[0][0]:
# Not need to execute any code because the first breakpoint
# is set in the first line with code
return
self.debug_command('continue')

@Slot()
def re_run_file(self):
Expand Down
14 changes: 10 additions & 4 deletions spyder/utils/ipython/spyder_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,6 @@ def save_namespace(self, filename):
return iofunctions.save(data, filename)

# --- For Pdb
def get_pdb_step(self):
"""Return info about pdb current frame"""
return self._pdb_step

def publish_pdb_state(self):
"""
Publish Variable Explorer state and Pdb step through
Expand All @@ -222,6 +218,16 @@ def publish_pdb_state(self):
step = self._pdb_step)
publish_data({'__spy_pdb_state__': state})

def pdb_continue(self):
"""
Tell the console to run 'continue' after entering a
Pdb session to get to the first breakpoint.
Fixes issue 2034
"""
if self._pdb_obj:
publish_data({'__spy_pdb_continue__': True})

# --- For the Help plugin
def is_defined(self, obj, force_import=False):
"""Return True if object is defined in current namespace"""
Expand Down
49 changes: 27 additions & 22 deletions spyder/utils/site/sitecustomize.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ def __init__(self, *args, **kwargs):
class SpyderPdb(pdb.Pdb):

send_initial_notification = True
starting = True

def set_spyder_breakpoints(self):
self.clear_all_breaks()
Expand Down Expand Up @@ -341,7 +342,7 @@ def notify_spyder(self, frame):
return

from IPython.core.getipython import get_ipython
ipython_shell = get_ipython()
kernel = get_ipython().kernel

# Get filename and line number of the current frame
fname = self.canonic(frame.f_code.co_filename)
Expand All @@ -352,18 +353,31 @@ def notify_spyder(self, frame):
pass
lineno = frame.f_lineno

# Jump to first breakpoint.
# Fixes issue 2034
if self.starting:
# Only run this after a Pdb session is created
self.starting = False

# Get all breakpoints for the file we're going to debug
breaks = self.get_file_breaks(frame.f_code.co_filename)

# Do 'continue' if the first breakpoint is *not* placed
# where the debugger is going to land.
# Fixes issue 4681
if breaks and lineno != breaks[0] and osp.isfile(fname):
kernel.pdb_continue()

# Set step of the current frame (if any)
step = {}
if isinstance(fname, basestring) and isinstance(lineno, int):
if osp.isfile(fname):
if ipython_shell:
step = dict(fname=fname, lineno=lineno)
step = dict(fname=fname, lineno=lineno)

# Publish Pdb state so we can update the Variable Explorer
# and the Editor on the Spyder side
if ipython_shell:
ipython_shell.kernel._pdb_step = step
ipython_shell.kernel.publish_pdb_state()
kernel._pdb_step = step
kernel.publish_pdb_state()

pdb.Pdb = SpyderPdb

Expand Down Expand Up @@ -452,10 +466,8 @@ def reset(self):
self._old_Pdb_reset()

from IPython.core.getipython import get_ipython
ipython_shell = get_ipython()
if ipython_shell:
ipython_shell.kernel._register_pdb_session(self)

kernel = get_ipython().kernel
kernel._register_pdb_session(self)
self.set_spyder_breakpoints()


Expand Down Expand Up @@ -581,8 +593,7 @@ def clear_post_mortem():
"""
from IPython.core.getipython import get_ipython
ipython_shell = get_ipython()
if ipython_shell:
ipython_shell.set_custom_exc((), None)
ipython_shell.set_custom_exc((), None)


def post_mortem_excepthook(type, value, tb):
Expand Down Expand Up @@ -638,16 +649,10 @@ def ipython_post_mortem_debug(shell, etype, evalue, tb,
# runfile and debugfile commands
#==============================================================================
def _get_globals():
"""Return current Python interpreter globals namespace"""
from __main__ import __dict__ as namespace
shell = namespace.get('__ipythonshell__')
if shell is not None and hasattr(shell, 'user_ns'):
# IPython 0.13+ kernel
return shell.user_ns
else:
# Python interpreter
return namespace
return namespace
"""Return current namespace"""
from IPython.core.getipython import get_ipython
ipython_shell = get_ipython()
return ipython_shell.user_ns


def runfile(filename, args=None, wdir=None, namespace=None, post_mortem=False):
Expand Down
6 changes: 6 additions & 0 deletions spyder/widgets/ipythonconsole/namespacebrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ def _handle_data_message(self, msg):
if pdb_state is not None and isinstance(pdb_state, dict):
self.refresh_from_pdb(pdb_state)

# Run Pdb continue to get to the first breakpoint
# Fixes 2034
pdb_continue = data.get('__spy_pdb_continue__', None)
if pdb_continue:
self.write_to_stdin('continue')

# ---- Private API (overrode by us) ----------------------------
def _handle_execute_reply(self, msg):
"""
Expand Down

0 comments on commit 7ce6111

Please sign in to comment.