Skip to content

Commit

Permalink
Merge pull request #10010 from impact27/save_history_pdb
Browse files Browse the repository at this point in the history
PR: Add permanent history to the debugger

[ci skip]
  • Loading branch information
ccordoba12 authored Aug 23, 2019
2 parents 287030d + 940a4e0 commit d672bb5
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 11 deletions.
28 changes: 26 additions & 2 deletions spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,10 +644,11 @@ def test_request_syspath(ipyconsole, qtbot, tmpdir):
@pytest.mark.slow
@flaky(max_runs=10)
@pytest.mark.skipif(os.name == 'nt', reason="It doesn't work on Windows")
def test_browse_history_dbg(ipyconsole, qtbot):
def test_save_history_dbg(ipyconsole, qtbot):
"""Test that browsing command history is working while debugging."""
shell = ipyconsole.get_current_shellwidget()
qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
qtbot.waitUntil(lambda: shell._prompt_html is not None,
timeout=SHELL_TIMEOUT)

# Give focus to the widget that's going to receive clicks
control = ipyconsole.get_focus_widget()
Expand Down Expand Up @@ -683,6 +684,29 @@ def test_browse_history_dbg(ipyconsole, qtbot):
qtbot.keyClick(control, Qt.Key_Up)
assert '!aa = 10' in control.toPlainText()

# Open new widget
ipyconsole.create_new_client()

shell = ipyconsole.get_current_shellwidget()
qtbot.waitUntil(lambda: shell._prompt_html is not None,
timeout=SHELL_TIMEOUT)

# Give focus to the widget that's going to receive clicks
control = ipyconsole.get_focus_widget()
control.setFocus()

# Generate a traceback and enter debugging mode
with qtbot.waitSignal(shell.executed):
shell.execute('1/0')

shell.execute('%debug')
qtbot.wait(1000)

# Press Up arrow button and assert we get the last
# introduced command
qtbot.keyClick(control, Qt.Key_Up)
assert '!aa = 10' in control.toPlainText()


@pytest.mark.slow
@flaky(max_runs=3)
Expand Down
1 change: 0 additions & 1 deletion spyder/plugins/ipythonconsole/widgets/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
# (see spyder/__init__.py for details)

"""Control widgets used by ShellWidget"""

from qtpy.QtCore import Qt, Signal
from qtpy.QtWidgets import QTextEdit

Expand Down
60 changes: 53 additions & 7 deletions spyder/plugins/ipythonconsole/widgets/debugging.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,36 @@
mode and Spyder
"""

import pdb
import re
import pdb

from IPython.core.history import HistoryManager
from qtpy.QtCore import Qt
from qtconsole.rich_jupyter_widget import RichJupyterWidget

from spyder.config.base import get_conf_path
from spyder.config.manager import CONF


class PdbHistory(HistoryManager):

def _get_hist_file_name(self, profile=None):
"""
Get default pdb history file name.
The profile parameter is ignored, but must exist for compatibility with
the parent class.
"""
return get_conf_path('pdb_history.sqlite')


class DebuggingWidget(RichJupyterWidget):
"""
Widget with the necessary attributes and methods to handle
communications between a console in debugging mode and
Spyder
"""
PDB_HIST_MAX = 400

def __init__(self, *args, **kwargs):
super(DebuggingWidget, self).__init__(*args, **kwargs)
Expand All @@ -35,13 +50,22 @@ def __init__(self, *args, **kwargs):
self._previous_prompt = None
self._input_queue = []
self._input_ready = False
self._last_pdb_cmd = ''
self._pdb_line_num = 0
self.pdb_history_file = PdbHistory()
self._control.history = [
line[-1] for line in self.pdb_history_file.get_tail(
self.PDB_HIST_MAX, include_latest=True)]

def set_queued_input(self, client):
"""Change the kernel client input function queue calls."""
old_input = client.input

def queued_input(string):
"""If input is not ready, save it in a queue."""
if not string.strip():
# Must get the last genuine command
string = self._last_pdb_cmd
if self._input_ready:
self._input_ready = False
return old_input(string)
Expand All @@ -54,6 +78,7 @@ def _debugging_hook(self, debugging):
"""Catches debugging state."""
# If debugging starts or stops, clear the input queue.
self._input_queue = []
self._pdb_line_num = 0

# --- Public API --------------------------------------------------
def write_to_stdin(self, line):
Expand Down Expand Up @@ -121,13 +146,16 @@ def _handle_input_request(self, msg):
return

def callback(line):
if not self.is_debugging():
return self.kernel_client.input(line)
line = line.strip()

# Save history to browse it later
if not (len(self._control.history) > 0
and self._control.history[-1] == line):
# do not save pdb commands
cmd = line.split(" ")[0]
if cmd and "do_" + cmd not in dir(pdb.Pdb):
self._control.history.append(line)
self._pdb_line_num += 1
self.add_to_pdb_history(self._pdb_line_num, line)

if line:
self._last_pdb_cmd = line

# must match ConsoleWidget.do_execute
self._executing = True
Expand Down Expand Up @@ -191,3 +219,21 @@ def _event_filter_console_keypress(self, event):
def is_debugging(self):
"""Check if we are debugging."""
return self.spyder_kernel_comm._debugging

def add_to_pdb_history(self, line_num, line):
"""Add command to history"""
self._control.histidx = None
if not line:
return
line = line.strip()

# If repeated line
if len(self._control.history) > 0 and self._control.history[-1] == line:
return

cmd = line.split(" ")[0]
args = line.split(" ")[1:]
is_pdb_cmd = "do_" + cmd in dir(pdb.Pdb)
if cmd and (not is_pdb_cmd or len(args) > 0):
self._control.history.append(line)
self.pdb_history_file.store_inputs(line_num, line)
3 changes: 2 additions & 1 deletion spyder/widgets/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -1377,13 +1377,14 @@ def browse_history(self, backward):
text, self.histidx = self.find_in_history(tocursor, self.histidx,
backward)
if text is not None:
text = text.strip()
if self.hist_wholeline:
self.clear_line()
self.insert_text(text)
else:
cursor_position = self.get_position('cursor')
# Removing text from cursor to the end of the line
self.remove_text('cursor', 'eol')
self.remove_text('cursor', 'eof')
# Inserting history text
self.insert_text(text)
self.set_cursor_position(cursor_position)
Expand Down

0 comments on commit d672bb5

Please sign in to comment.