Skip to content

Commit

Permalink
Merge: Add post mortem debug behavior when running a file (pull request
Browse files Browse the repository at this point in the history
#13)

Fixes issue 1387
  • Loading branch information
ccordoba12 committed Jan 12, 2015
2 parents d86809a + 797e9b8 commit 797a3a0
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 60 deletions.
1 change: 1 addition & 0 deletions spyderlib/plugins/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ def run_script(self, filename=None, silent=False, set_focus=False,
filename = osp.basename(filename)
else:
return
debug_print(args)
filename = osp.abspath(filename)
rbs = remove_backslashes
command = "runfile('%s', args='%s')" % (rbs(filename), rbs(args))
Expand Down
18 changes: 10 additions & 8 deletions spyderlib/plugins/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,12 +333,12 @@ class Editor(SpyderPluginWidget):
DISABLE_ACTIONS_WHEN_HIDDEN = False # SpyderPluginWidget class attribute

# Signals
run_in_current_ipyclient = Signal(str, str, str, bool)
run_in_current_ipyclient = Signal(str, str, str, bool, bool)
exec_in_extconsole = Signal(str, bool)
redirect_stdio = Signal(bool)
open_dir = Signal(str)
breakpoints_saved = Signal()
run_in_current_extconsole = Signal(str, str, str, bool)
run_in_current_extconsole = Signal(str, str, str, bool, bool)

def __init__(self, parent, ignore_last_opened_files=False):
SpyderPluginWidget.__init__(self, parent)
Expand Down Expand Up @@ -2046,13 +2046,14 @@ def run_file(self, debug=False):
args = runconf.get_arguments()
python_args = runconf.get_python_arguments()
interact = runconf.interact
post_mortem = runconf.post_mortem
current = runconf.current
systerm = runconf.systerm

python = True # Note: in the future, it may be useful to run
# something in a terminal instead of a Python interp.
self.__last_ec_exec = (fname, wdir, args, interact, debug,
python, python_args, current, systerm)
python, python_args, current, systerm, post_mortem)
self.re_run_file()
if not interact and not debug:
# If external console dockwidget is hidden, it will be
Expand All @@ -2077,21 +2078,22 @@ def re_run_file(self):
if self.__last_ec_exec is None:
return
(fname, wdir, args, interact, debug,
python, python_args, current, systerm) = self.__last_ec_exec
python, python_args, current, systerm, post_mortem) = self.__last_ec_exec
if current:
if self.main.ipyconsole is not None:
if self.main.last_console_plugin_focus_was_python:
self.run_in_current_extconsole.emit(fname, wdir, args,
debug)
debug, post_mortem)
else:
self.run_in_current_ipyclient.emit(fname, wdir, args,
debug)
debug, post_mortem)
else:
self.run_in_current_extconsole.emit(fname, wdir, args, debug)
self.run_in_current_extconsole.emit(fname, wdir, args, debug,
post_mortem)
else:
self.main.open_external_console(fname, wdir, args, interact,
debug, python, python_args,
systerm)
systerm, post_mortem)

@Slot()
def run_selection(self):
Expand Down
10 changes: 7 additions & 3 deletions spyderlib/plugins/externalconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,8 @@ def get_running_python_shell(self):
else:
return shellwidgets[0].shell

def run_script_in_current_shell(self, filename, wdir, args, debug):
def run_script_in_current_shell(self, filename, wdir, args, debug,
post_mortem):
"""Run script in current shell, if any"""
norm = lambda text: remove_backslashes(to_text_string(text))
line = "%s('%s'" % ('debugfile' if debug else 'runfile',
Expand All @@ -659,6 +660,8 @@ def run_script_in_current_shell(self, filename, wdir, args, debug):
line += ", args=r'%s'" % norm(args)
if wdir:
line += ", wdir=r'%s'" % norm(wdir)
if post_mortem:
line += ', post_mortem=True'
line += ")"
if not self.execute_python_code(line, interpreter_only=True):
QMessageBox.warning(self, _('Warning'),
Expand Down Expand Up @@ -710,7 +713,7 @@ def set_spyder_breakpoints(self):

def start(self, fname, wdir=None, args='', interact=False, debug=False,
python=True, ipykernel=False, ipyclient=None,
give_ipyclient_focus=True, python_args=''):
give_ipyclient_focus=True, python_args='', post_mortem=True):
"""
Start new console
Expand Down Expand Up @@ -807,7 +810,8 @@ def start(self, fname, wdir=None, args='', interact=False, debug=False,
else:
sa_settings = None
shellwidget = ExternalPythonShell(self, fname, wdir,
interact, debug, path=pythonpath,
interact, debug, post_mortem=post_mortem,
path=pythonpath,
python_args=python_args,
ipykernel=ipykernel,
arguments=args, stand_alone=sa_settings,
Expand Down
6 changes: 4 additions & 2 deletions spyderlib/plugins/ipythonconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,8 @@ def get_current_client(self):
if client is not None:
return client

def run_script_in_current_client(self, filename, wdir, args, debug):
def run_script_in_current_client(self, filename, wdir, args, debug,
post_mortem):
"""Run script in current client, if any"""
norm = lambda text: remove_backslashes(to_text_string(text))
client = self.get_current_client()
Expand All @@ -781,6 +782,8 @@ def run_script_in_current_client(self, filename, wdir, args, debug):
line += ", args='%s'" % norm(args)
if wdir:
line += ", wdir='%s'" % norm(wdir)
if post_mortem:
line += ", post_mortem=True"
line += ")"
else: # External kernels, use %run
line = "%run "
Expand Down Expand Up @@ -1200,4 +1203,3 @@ def show_quickref(self):
# elif shellwidget:
# shellwidget.shell.drop_pathlist(pathlist)
# event.acceptProposedAction()

12 changes: 10 additions & 2 deletions spyderlib/plugins/runconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ def __init__(self, fname=None):
self.current = None
self.systerm = None
self.interact = None
self.show_kill_warning = None
self.show_kill_warning =None
self.post_mortem = None
self.python_args = None
self.python_args_enabled = None
self.set(CONF.get('run', 'defaultconfiguration', default={}))
Expand All @@ -76,6 +77,7 @@ def set(self, options):
CONF.get('run', SYSTERM_INTERPRETER_OPTION, False))
self.interact = options.get('interact', False)
self.show_kill_warning = options.get('show_kill_warning', True)
self.post_mortem = options.get('post_mortem', False)
self.python_args = options.get('python_args', '')
self.python_args_enabled = options.get('python_args/enabled', False)

Expand All @@ -89,6 +91,7 @@ def get(self):
'systerm': self.systerm,
'interact': self.interact,
'show_kill_warning': self.show_kill_warning,
'post_mortem': self.post_mortem,
'python_args/enabled': self.python_args_enabled,
'python_args': self.python_args,
}
Expand Down Expand Up @@ -171,6 +174,9 @@ def __init__(self, parent=None):
browse_btn.clicked.connect(self.select_directory)
wd_layout.addWidget(browse_btn)
common_layout.addLayout(wd_layout, 1, 1)
self.post_mortem_cb = QCheckBox(_("Enter post mortem debugging"
" for uncaught exceptions"))
common_layout.addWidget(self.post_mortem_cb)

# --- Interpreter ---
interpreter_group = QGroupBox(_("Console"))
Expand All @@ -194,6 +200,7 @@ def __init__(self, parent=None):

self.show_kill_warning_cb = QCheckBox(_("Show warning when killing"
" running process"))

new_layout.addWidget(self.show_kill_warning_cb, 2, 0, 1, -1)
self.pclo_cb = QCheckBox(_("Command line options:"))
new_layout.addWidget(self.pclo_cb, 3, 0)
Expand All @@ -204,7 +211,6 @@ def __init__(self, parent=None):
"other options you set here"))
new_layout.addWidget(self.pclo_edit, 3, 1)

#TODO: Add option for "Post-mortem debugging"

# Checkbox to preserve the old behavior, i.e. always open the dialog
# on first run
Expand Down Expand Up @@ -247,6 +253,7 @@ def set(self, options):
self.dedicated_radio.setChecked(True)
self.interact_cb.setChecked(self.runconf.interact)
self.show_kill_warning_cb.setChecked(self.runconf.show_kill_warning)
self.post_mortem_cb.setChecked(self.runconf.post_mortem)
self.pclo_cb.setChecked(self.runconf.python_args_enabled)
self.pclo_edit.setText(self.runconf.python_args)

Expand All @@ -259,6 +266,7 @@ def get(self):
self.runconf.systerm = self.systerm_radio.isChecked()
self.runconf.interact = self.interact_cb.isChecked()
self.runconf.show_kill_warning = self.show_kill_warning_cb.isChecked()
self.runconf.post_mortem = self.post_mortem_cb.isChecked()
self.runconf.python_args_enabled = self.pclo_cb.isChecked()
self.runconf.python_args = to_text_string(self.pclo_edit.text())
return self.runconf.get()
Expand Down
4 changes: 2 additions & 2 deletions spyderlib/spyder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1876,7 +1876,7 @@ def redirect_internalshell_stdio(self, state):
self.console.shell.interpreter.restore_stds()

def open_external_console(self, fname, wdir, args, interact, debug, python,
python_args, systerm):
python_args, systerm, post_mortem=False):
"""Open external console"""
if systerm:
# Running script in an external system terminal
Expand All @@ -1894,7 +1894,7 @@ def open_external_console(self, fname, wdir, args, interact, debug, python,
self.extconsole.start(
fname=to_text_string(fname), wdir=to_text_string(wdir),
args=to_text_string(args), interact=interact,
debug=debug, python=python,
debug=debug, python=python, post_mortem=post_mortem,
python_args=to_text_string(python_args) )

def execute_in_external_console(self, lines, focus_to_editor):
Expand Down
19 changes: 16 additions & 3 deletions spyderlib/widgets/externalshell/pythonshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ class ExternalPythonShell(ExternalShellBase):
sig_finished = Signal()

def __init__(self, parent=None, fname=None, wdir=None,
interact=False, debug=False, path=[], python_args='',
interact=False, debug=False, post_mortem=False,
path=[], python_args='',
ipykernel=False, arguments='', stand_alone=None,
umr_enabled=True, umr_namelist=[], umr_verbose=True,
pythonstartup=None, pythonexecutable=None,
Expand Down Expand Up @@ -244,11 +245,14 @@ def __init__(self, parent=None, fname=None, wdir=None,
self.interact_action.setChecked(self.interact)
self.debug_action.setChecked(debug)


self.introspection_socket = None
self.is_interpreter = fname is None

if self.is_interpreter:
self.terminate_button.hide()

self.post_mortem_action.setChecked(post_mortem and not self.is_interpreter)

# Additional python path list
self.path = path
Expand Down Expand Up @@ -303,9 +307,12 @@ def get_options_menu(self):
self.debug_action.setCheckable(True)
self.args_action = create_action(self, _("Arguments..."),
triggered=self.get_arguments)
self.post_mortem_action = create_action(self, _("Post Mortem Debug"))
self.post_mortem_action.setCheckable(True)
run_settings_menu = QMenu(_("Run settings"), self)
add_actions(run_settings_menu,
(self.interact_action, self.debug_action, self.args_action))
(self.interact_action, self.debug_action, self.args_action,
self.post_mortem_action))
self.cwd_button = create_action(self, _("Working directory"),
icon=get_std_icon('DirOpenIcon'),
tip=_("Set current working directory"),
Expand Down Expand Up @@ -357,6 +364,7 @@ def set_buttons_runnning_state(self, state):
self.interact_action.setEnabled(not state and not self.is_interpreter)
self.debug_action.setEnabled(not state and not self.is_interpreter)
self.args_action.setEnabled(not state and not self.is_interpreter)
self.post_mortem_action.setEnabled(not state and not self.is_interpreter)
if state:
if self.arguments:
argstr = _("Arguments: %s") % self.arguments
Expand Down Expand Up @@ -420,13 +428,18 @@ def create_process(self):
if self.pythonstartup:
env.append('PYTHONSTARTUP=%s' % self.pythonstartup)

#-------------------------Python specific-------------------------------
# Post mortem debugging
if self.post_mortem_action.isChecked():
env.append('SPYDER_EXCEPTHOOK=True')

# Set standard input/output encoding for Python consoles
# (IPython handles it on its own)
# See http://stackoverflow.com/q/26312400/438386, specifically
# the comments of Martijn Pieters
if not self.is_ipykernel:
env.append('PYTHONIOENCODING=UTF-8')

# Monitor
if self.monitor_enabled:
env.append('SPYDER_SHELL_ID=%s' % id(self))
Expand Down
Loading

0 comments on commit 797a3a0

Please sign in to comment.