Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Move debug cell / file / config to Debugger plugin #19181

Merged
merged 13 commits into from
Aug 31, 2022
10 changes: 5 additions & 5 deletions spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def main_window(request, tmpdir, qtbot):
CONF.set('ipython_console', 'pylab/inline/figure_format', 0)

# Set exclamation mark to True
CONF.set('ipython_console', 'pdb_use_exclamation_mark', True)
CONF.set('debugger', 'pdb_use_exclamation_mark', True)

# Check if we need to use introspection in a given test
# (it's faster and less memory consuming not to use it!)
Expand Down Expand Up @@ -4763,10 +4763,10 @@ def test_prevent_closing(main_window, qtbot):
with qtbot.waitSignal(shell.executed):
qtbot.mouseClick(debug_button, Qt.LeftButton)

CONF.set('ipython_console', 'pdb_prevent_closing', False)
CONF.set('debugger', 'pdb_prevent_closing', False)
# Check we can close a file we debug if the option is disabled
assert main_window.editor.get_current_editorstack().close_file()
CONF.set('ipython_console', 'pdb_prevent_closing', True)
CONF.set('debugger', 'pdb_prevent_closing', True)
# Check we are still debugging
assert shell.is_debugging()

Expand All @@ -4777,7 +4777,7 @@ def test_continue_first_line(main_window, qtbot):
"""
Check we can bypass prevent closing.
"""
CONF.set('ipython_console', 'pdb_stop_first_line', False)
CONF.set('debugger', 'pdb_stop_first_line', False)
code = "print('a =', 1 + 6)\nprint('b =', 1 + 8)\n"

# Wait until the window is fully up
Expand Down Expand Up @@ -4805,7 +4805,7 @@ def test_continue_first_line(main_window, qtbot):
qtbot.mouseClick(debug_button, Qt.LeftButton)
# The debugging should finish
qtbot.waitUntil(lambda: not shell.is_debugging())
CONF.set('ipython_console', 'pdb_stop_first_line', True)
CONF.set('debugger', 'pdb_stop_first_line', True)

# Check everything was executed
qtbot.waitUntil(lambda: "a = 7" in shell._control.toPlainText())
Expand Down
26 changes: 13 additions & 13 deletions spyder/config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,6 @@
# that generate a lot of Command Prompts while running,
# and that's extremely annoying for Windows users.
'hide_cmd_windows': True,
'pdb_prevent_closing': True,
'pdb_ignore_lib': False,
'pdb_execute_events': True,
'pdb_use_exclamation_mark': True,
'pdb_stop_first_line': True
}),
('variable_explorer',
{
Expand All @@ -188,6 +183,11 @@
'exclude_internal': True,
'capture_locals': False,
'show_locals_on_click': False,
'pdb_prevent_closing': True,
'pdb_ignore_lib': False,
'pdb_execute_events': True,
'pdb_use_exclamation_mark': True,
'pdb_stop_first_line': True
}),
('plots',
{
Expand Down Expand Up @@ -386,12 +386,6 @@
# -- In plugins/editor
'_/file switcher': 'Ctrl+P',
'_/symbol finder': 'Ctrl+Alt+P',
'_/debug': "Ctrl+F5",
'_/debug step over': "Ctrl+F10",
'_/debug continue': "Ctrl+F12",
'_/debug step into': "Ctrl+F11",
'_/debug step return': "Ctrl+Shift+F11",
'_/debug exit': "Ctrl+Shift+F12",
'_/run': "F5",
'_/configure': "Ctrl+F6",
'_/re-run last script': "F6",
Expand Down Expand Up @@ -489,7 +483,6 @@
'editor/close file 2': "Ctrl+F4",
'editor/run cell': CTRL + '+Return',
'editor/run cell and advance': 'Shift+Return',
'editor/debug cell': 'Alt+Shift+Return',
'editor/go to next cell': 'Ctrl+Down',
'editor/go to previous cell': 'Ctrl+Up',
'editor/re-run last cell': 'Alt+Return',
Expand Down Expand Up @@ -529,6 +522,13 @@
# ---- In widgets/debugger/framesbrowser.py ----
'debugger/refresh': 'Ctrl+R',
'debugger/search': 'Ctrl+F',
'debugger/debug file': "Ctrl+F5",
'debugger/debug cell': 'Alt+Shift+Return',
'debugger/next': "Ctrl+F10",
'debugger/continue': "Ctrl+F12",
'debugger/step': "Ctrl+F11",
'debugger/return': "Ctrl+Shift+F11",
'debugger/stop': "Ctrl+Shift+F12",
# ---- In widgets/plots/figurebrowser.py ----
'plots/copy': 'Ctrl+C',
'plots/previous figure': 'Ctrl+PgUp',
Expand Down Expand Up @@ -648,4 +648,4 @@
# or if you want to *rename* options, then you need to do a MAJOR update in
# version, e.g. from 3.0.0 to 4.0.0
# 3. You don't need to touch this value if you're just adding a new option
CONF_VERSION = '71.0.0'
CONF_VERSION = '72.0.0'
76 changes: 71 additions & 5 deletions spyder/plugins/debugger/confpage.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"""Debugger Plugin Configuration Page."""

# Third party imports
from qtpy.QtWidgets import QGroupBox, QVBoxLayout
from qtpy.QtCore import Qt
from qtpy.QtWidgets import QGroupBox, QVBoxLayout, QLabel

# Local imports
from spyder.config.base import _
Expand All @@ -17,12 +18,75 @@
class DebuggerConfigPage(PluginConfigPage):

def setup_page(self):
filter_group = QGroupBox(_("Filter"))
newcb = self.create_checkbox

# ---- Debug ----
# Pdb run lines Group
pdb_run_lines_group = QGroupBox(_("Run code while debugging"))
pdb_run_lines_label = QLabel(_(
"You can run several lines of code on each new prompt while "
"debugging. Please introduce each one separated by semicolons and "
"a space, for example:<br>"
"<i>import matplotlib.pyplot as plt; import numpy as np</i>")
)
pdb_run_lines_label.setWordWrap(True)
pdb_run_lines_edit = self.create_lineedit(
_("Lines:"), 'startup/pdb_run_lines', '', alignment=Qt.Horizontal)

pdb_run_lines_layout = QVBoxLayout()
pdb_run_lines_layout.addWidget(pdb_run_lines_label)
pdb_run_lines_layout.addWidget(pdb_run_lines_edit)
pdb_run_lines_group.setLayout(pdb_run_lines_layout)

# Debug Group
debug_group = QGroupBox(_("Debug"))
debug_layout = QVBoxLayout()

prevent_closing_box = newcb(
_("Prevent editor from closing files while debugging"),
'pdb_prevent_closing',
tip=_("This option prevents the user from closing a file while"
" it is debugged."))
debug_layout.addWidget(prevent_closing_box)

continue_box = newcb(
_("Stop debugging on first line of files without breakpoints"),
'pdb_stop_first_line',
tip=_("This option lets you decide if the debugger should"
" stop on the first line while debugging if no breakpoints"
" are present."))
debug_layout.addWidget(continue_box)

libraries_box = newcb(
_("Ignore Python libraries while debugging"), 'pdb_ignore_lib',
tip=_("This option lets you decide if the debugger should "
"ignore the system libraries while debugging."))
debug_layout.addWidget(libraries_box)

execute_events_box = newcb(
_("Process execute events while debugging"), 'pdb_execute_events',
tip=_("This option lets you decide if the debugger should "
"process the 'execute events' after each prompt, such as "
"matplotlib <tt>show</tt> command."))
debug_layout.addWidget(execute_events_box)

exclamation_mark_box = newcb(
_("Use exclamation mark prefix for Pdb commands"),
'pdb_use_exclamation_mark',
tip=_("This option lets you decide if the Pdb commands should "
"be prefixed by an exclamation mark. This helps in "
"separating Pdb commands from Python code."))
debug_layout.addWidget(exclamation_mark_box)

debug_group.setLayout(debug_layout)

filter_group = QGroupBox(_("Execution Inspector"))
filter_data = [
('exclude_internal', _("Exclude internal threads and frames")),
('capture_locals', _("Capture frames locals")),
('exclude_internal', _("Exclude internal frames when inspecting execution")),
('capture_locals', _("Capture locals when inspecting execution")),
('show_locals_on_click',
_("Show locals in Variable explorer when frame is selected")),
_("Show selected frame locals from inspection "
"in the Variable Explorer")),
]
filter_boxes = [self.create_checkbox(text, option)
for option, text in filter_data]
Expand All @@ -33,6 +97,8 @@ def setup_page(self):
filter_group.setLayout(filter_layout)

vlayout = QVBoxLayout()
vlayout.addWidget(debug_group)
vlayout.addWidget(pdb_run_lines_group)
vlayout.addWidget(filter_group)
vlayout.addStretch(1)
self.setLayout(vlayout)
92 changes: 89 additions & 3 deletions spyder/plugins/debugger/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

"""Debugger Plugin."""

# Third-party imports
from qtpy.QtCore import Slot

# Local imports
from spyder.config.base import _
from spyder.api.plugins import Plugins, SpyderDockablePlugin
Expand All @@ -15,14 +18,18 @@
from spyder.plugins.debugger.widgets.main_widget import (
DebuggerWidget)
from spyder.api.shellconnect.mixins import ShellConnectMixin
from spyder.utils.qthelpers import MENU_SEPARATOR
from spyder.config.manager import CONF
from spyder.plugins.mainmenu.api import ApplicationMenus
from spyder.plugins.debugger.widgets.main_widget import DebuggerToolbarActions


class Debugger(SpyderDockablePlugin, ShellConnectMixin):
"""Debugger plugin."""

NAME = 'debugger'
REQUIRES = [Plugins.IPythonConsole, Plugins.Preferences]
OPTIONAL = [Plugins.Editor, Plugins.VariableExplorer]
OPTIONAL = [Plugins.Editor, Plugins.VariableExplorer, Plugins.MainMenu]
TABIFY = [Plugins.VariableExplorer, Plugins.Help]
WIDGET_CLASS = DebuggerWidget
CONF_SECTION = NAME
Expand Down Expand Up @@ -58,12 +65,41 @@ def on_preferences_teardown(self):
@on_plugin_available(plugin=Plugins.Editor)
def on_editor_available(self):
editor = self.get_plugin(Plugins.Editor)
self.get_widget().edit_goto.connect(editor.load)
widget = self.get_widget()

widget.edit_goto.connect(editor.load)
widget.sig_debug_file.connect(self.debug_file)
widget.sig_debug_cell.connect(self.debug_cell)

names = [
DebuggerToolbarActions.DebugCurrentFile,
DebuggerToolbarActions.DebugCurrentCell,
]
for name in names:
action = widget.get_action(name)
CONF.config_shortcut(
action.trigger,
context=self.CONF_SECTION,
name=name,
parent=editor)
self.main.debug_toolbar_actions += [action]

@on_plugin_teardown(plugin=Plugins.Editor)
def on_editor_teardown(self):
editor = self.get_plugin(Plugins.Editor)
self.get_widget().edit_goto.disconnect(editor.load)
widget = self.get_widget()

widget.edit_goto.disconnect(editor.load)
widget.sig_debug_file.disconnect(self.debug_file)
widget.sig_debug_cell.disconnect(self.debug_cell)

names = [
DebuggerToolbarActions.DebugCurrentFile,
DebuggerToolbarActions.DebugCurrentCell,
]
for name in names:
action = widget.get_action(name)
self.main.debug_toolbar_actions.remove(action)

@on_plugin_available(plugin=Plugins.VariableExplorer)
def on_variable_explorer_available(self):
Expand All @@ -75,8 +111,58 @@ def on_variable_explorer_teardown(self):
self.get_widget().sig_show_namespace.disconnect(
self.show_namespace_in_variable_explorer)

@on_plugin_available(plugin=Plugins.MainMenu)
def on_main_menu_available(self):
widget = self.get_widget()
debug_file_action = widget.get_action(
DebuggerToolbarActions.DebugCurrentFile)
debug_cell_action = widget.get_action(
DebuggerToolbarActions.DebugCurrentCell)

self.main.debug_menu_actions = [
debug_file_action,
debug_cell_action,
MENU_SEPARATOR,
] + self.main.debug_menu_actions

@on_plugin_teardown(plugin=Plugins.MainMenu)
def on_main_menu_teardown(self):
mainmenu = self.get_plugin(Plugins.MainMenu)

mainmenu.remove_item_from_application_menu(
DebuggerToolbarActions.DebugCurrentFile,
menu_id=ApplicationMenus.Debug
)
mainmenu.remove_item_from_application_menu(
DebuggerToolbarActions.DebugCurrentCell,
menu_id=ApplicationMenus.Debug
)

# ---- Public API
# ------------------------------------------------------------------------
@Slot()
def debug_file(self):
"""
Debug current file.

It should only be called when an editor is available.
"""
editor = self.get_plugin(Plugins.Editor, error=False)
if editor:
editor.switch_to_plugin()
editor.run_file(method="debugfile")

@Slot()
def debug_cell(self):
"""
Debug current cell.

It should only be called when an editor is available.
"""
editor = self.get_plugin(Plugins.Editor, error=False)
if editor:
editor.run_cell(method="debugcell")

def show_namespace_in_variable_explorer(self, namespace, shellwidget):
"""
Find the right variable explorer widget and show the namespace.
Expand Down
29 changes: 29 additions & 0 deletions spyder/plugins/debugger/widgets/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ class DebuggerWidgetActions:
ToggleLocalsOnClick = 'toggle_show_locals_on_click_action'


class DebuggerToolbarActions:
DebugCurrentFile = 'debug file'
impact27 marked this conversation as resolved.
Show resolved Hide resolved
DebugCurrentCell = 'debug cell'


class DebuggerWidgetOptionsMenuSections:
Display = 'excludes_section'
Highlight = 'highlight_section'
Expand Down Expand Up @@ -77,6 +82,12 @@ class DebuggerWidget(ShellConnectMainWidget):
edit_goto = Signal((str, int, str), (str, int, str, bool))
sig_show_namespace = Signal(dict, object)

sig_debug_file = Signal()
"""This signal is emitted to request the current file to be debugged."""

sig_debug_cell = Signal()
"""This signal is emitted to request the current cell to be debugged."""

def __init__(self, name=None, plugin=None, parent=None):
super().__init__(name, plugin, parent)

Expand Down Expand Up @@ -185,6 +196,24 @@ def setup(self):
register_shortcut=True
)

self.create_action(
DebuggerToolbarActions.DebugCurrentFile,
text=_("&Debug file"),
tip=_("Debug file"),
icon=self.create_icon('debug'),
triggered=self.sig_debug_file,
register_shortcut=True,
)

self.create_action(
DebuggerToolbarActions.DebugCurrentCell,
text=_("Debug cell"),
tip=_("Debug cell"),
icon=self.create_icon('debug_cell'),
triggered=self.sig_debug_cell,
register_shortcut=True,
)

# ---- Context menu actions
self.view_locals_action = self.create_action(
DebuggerContextMenuActions.ViewLocalsAction,
Expand Down
Loading