From 76eb1c8f83a208ed923cdb38e7fd7521ba8ba2eb Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 19 Jan 2017 23:13:38 -0500 Subject: [PATCH 01/13] Prevents showing errors. --- spyder/widgets/ipythonconsole/debugging.py | 32 ++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/spyder/widgets/ipythonconsole/debugging.py b/spyder/widgets/ipythonconsole/debugging.py index 93e44176019..24de4fa0add 100644 --- a/spyder/widgets/ipythonconsole/debugging.py +++ b/spyder/widgets/ipythonconsole/debugging.py @@ -47,16 +47,23 @@ def silent_exec_input(self, code): self._hidden = False # Emit signal - if 'pdb_step' in code and self._input_reply is not None: - fname = self._input_reply['fname'] - lineno = self._input_reply['lineno'] - self.sig_pdb_step.emit(fname, lineno) - elif 'get_namespace_view' in code: - view = self._input_reply - self.sig_namespace_view.emit(view) - elif 'get_var_properties' in code: - properties = self._input_reply - self.sig_var_properties.emit(properties) + if isinstance(self._input_reply, dict): + if 'pdb_step' in code and 'fname' in self._input_reply: + fname = self._input_reply['fname'] + lineno = self._input_reply['lineno'] + self.sig_pdb_step.emit(fname, lineno) + elif 'get_namespace_view' in code: + if not 'fname' in self._input_reply: + view = self._input_reply + else: + view = None + self.sig_namespace_view.emit(view) + elif 'get_var_properties' in code: + if not 'fname' in self._input_reply: + properties = self._input_reply + else: + properties = None + self.sig_var_properties.emit(properties) def write_to_stdin(self, line): """Send raw characters to the IPython kernel through stdin""" @@ -130,7 +137,10 @@ def _handle_stream(self, msg): reply = ast.literal_eval(text) except: reply = None - self._input_reply = reply + if not isinstance(reply, dict): + self._input_reply = None + else: + self._input_reply = reply self.sig_input_reply.emit() else: self._input_reply = None From 7a61fe07118316b11468ecb0c2e601b7c0bf3fc3 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 20 Jan 2017 22:50:35 -0500 Subject: [PATCH 02/13] Improvement in validations. --- spyder/widgets/ipythonconsole/debugging.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spyder/widgets/ipythonconsole/debugging.py b/spyder/widgets/ipythonconsole/debugging.py index 24de4fa0add..7226591d988 100644 --- a/spyder/widgets/ipythonconsole/debugging.py +++ b/spyder/widgets/ipythonconsole/debugging.py @@ -12,10 +12,12 @@ import ast from qtpy.QtCore import QEventLoop +from qtpy.QtWidgets import QMessageBox from qtconsole.rich_jupyter_widget import RichJupyterWidget from spyder.py3compat import to_text_string +from spyder.config.base import _ class DebuggingWidget(RichJupyterWidget): @@ -53,13 +55,13 @@ def silent_exec_input(self, code): lineno = self._input_reply['lineno'] self.sig_pdb_step.emit(fname, lineno) elif 'get_namespace_view' in code: - if not 'fname' in self._input_reply: + if 'fname' not in self._input_reply: view = self._input_reply else: view = None self.sig_namespace_view.emit(view) elif 'get_var_properties' in code: - if not 'fname' in self._input_reply: + if 'fname' not in self._input_reply: properties = self._input_reply else: properties = None From c92c4a3881a311d2aa418f616d97b6ad4db87986 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 3 Feb 2017 17:34:10 -0500 Subject: [PATCH 03/13] Adds a message and shutdown the kernel when a None response is handled. --- spyder/widgets/ipythonconsole/debugging.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spyder/widgets/ipythonconsole/debugging.py b/spyder/widgets/ipythonconsole/debugging.py index 7226591d988..a15e9095451 100644 --- a/spyder/widgets/ipythonconsole/debugging.py +++ b/spyder/widgets/ipythonconsole/debugging.py @@ -66,6 +66,15 @@ def silent_exec_input(self, code): else: properties = None self.sig_var_properties.emit(properties) + else: + self.kernel_client.iopub_channel.flush() + self.kernel_client.input('exit') + self.kernel_client.shutdown() + QMessageBox.information(self, "stdout/stdin invalid", + "The line runned changes stdout/stdin " + "making imposible to continue debugging. " + "

The debug of the file will " + "exit now.") def write_to_stdin(self, line): """Send raw characters to the IPython kernel through stdin""" From 26676cacf3df6c74b99a17dd51bd4da9bdc90058 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 10 Feb 2017 16:15:42 -0500 Subject: [PATCH 04/13] Use of the restart_kernel dialog. --- spyder/widgets/ipythonconsole/debugging.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/spyder/widgets/ipythonconsole/debugging.py b/spyder/widgets/ipythonconsole/debugging.py index a15e9095451..bf561560795 100644 --- a/spyder/widgets/ipythonconsole/debugging.py +++ b/spyder/widgets/ipythonconsole/debugging.py @@ -69,12 +69,9 @@ def silent_exec_input(self, code): else: self.kernel_client.iopub_channel.flush() self.kernel_client.input('exit') - self.kernel_client.shutdown() - QMessageBox.information(self, "stdout/stdin invalid", - "The line runned changes stdout/stdin " - "making imposible to continue debugging. " - "

The debug of the file will " - "exit now.") + self.restart_kernel(_("The line runned changes stdout/stdin " + "making imposible to continue debugging. "), + now=True) def write_to_stdin(self, line): """Send raw characters to the IPython kernel through stdin""" From 25339e919ecc6b2281ab56e4391f9285fcfd976c Mon Sep 17 00:00:00 2001 From: dalthviz Date: Sat, 18 Feb 2017 00:42:44 -0500 Subject: [PATCH 05/13] Restarting the kernel again. --- spyder/widgets/ipythonconsole/client.py | 6 +++--- spyder/widgets/ipythonconsole/debugging.py | 12 ++++++------ spyder/widgets/ipythonconsole/shell.py | 13 ++++++++++--- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py index 39d6d06d662..0601f0878bf 100644 --- a/spyder/widgets/ipythonconsole/client.py +++ b/spyder/widgets/ipythonconsole/client.py @@ -316,7 +316,7 @@ def interrupt_kernel(self): self.shellwidget.request_interrupt_kernel() @Slot() - def restart_kernel(self): + def restart_kernel(self, force=False): """ Restart the associanted kernel @@ -327,7 +327,7 @@ def restart_kernel(self): buttons = QMessageBox.Yes | QMessageBox.No result = QMessageBox.question(self, _('Restart kernel?'), message, buttons) - if result == QMessageBox.Yes: + if result == QMessageBox.Yes or force: sw = self.shellwidget if sw.kernel_manager: if self.infowidget.isVisible(): @@ -351,7 +351,7 @@ def restart_kernel(self): before_prompt=True ) - @Slot(str) + @Slot(str, bool) def kernel_restarted_message(self, msg): """Show kernel restarted/died messages.""" stderr = codecs.open(self.stderr_file, 'r', encoding='utf-8').read() diff --git a/spyder/widgets/ipythonconsole/debugging.py b/spyder/widgets/ipythonconsole/debugging.py index bf561560795..b39a7a27f3f 100644 --- a/spyder/widgets/ipythonconsole/debugging.py +++ b/spyder/widgets/ipythonconsole/debugging.py @@ -12,12 +12,10 @@ import ast from qtpy.QtCore import QEventLoop -from qtpy.QtWidgets import QMessageBox from qtconsole.rich_jupyter_widget import RichJupyterWidget from spyder.py3compat import to_text_string -from spyder.config.base import _ class DebuggingWidget(RichJupyterWidget): @@ -28,6 +26,7 @@ class DebuggingWidget(RichJupyterWidget): """ _input_reply = None + _input_none = False # --- Public API -------------------------------------------------- def silent_exec_input(self, code): @@ -68,14 +67,15 @@ def silent_exec_input(self, code): self.sig_var_properties.emit(properties) else: self.kernel_client.iopub_channel.flush() - self.kernel_client.input('exit') - self.restart_kernel(_("The line runned changes stdout/stdin " - "making imposible to continue debugging. "), - now=True) + self.write_to_stdin('exit') + self._input_reply = {} + self._input_none = True + self.kernel_client.shutdown() def write_to_stdin(self, line): """Send raw characters to the IPython kernel through stdin""" wait_loop = QEventLoop() + self.kernel_client.iopub_channel.flush() self.sig_prompt_ready.connect(wait_loop.quit) self.kernel_client.input(line) wait_loop.exec_() diff --git a/spyder/widgets/ipythonconsole/shell.py b/spyder/widgets/ipythonconsole/shell.py index 1b8bd5324f3..4d850ab979f 100644 --- a/spyder/widgets/ipythonconsole/shell.py +++ b/spyder/widgets/ipythonconsole/shell.py @@ -45,7 +45,7 @@ class ShellWidget(NamepaceBrowserWidget, HelpWidget, DebuggingWidget): focus_changed = Signal() new_client = Signal() sig_got_reply = Signal() - sig_kernel_restarted = Signal(str) + sig_kernel_restarted = Signal(str, bool) def __init__(self, ipyclient, additional_options, interpreter_versions, external_kernel, *args, **kw): @@ -282,8 +282,15 @@ def _banner_default(self): return self.short_banner() def _kernel_restarted_message(self, died=True): - msg = _("Kernel died, restarting") if died else _("Kernel restarting") - self.sig_kernel_restarted.emit(msg) + if self._input_none: + msg = _("Kernel restarting because an error raised " + "while debugging (stdout change)") + elif died: + msg = _("Kernel died, restarting") + else: + msg = _("Kernel restarting") + self.sig_kernel_restarted.emit(msg, self._input_none) + self._input_none = False #---- Qt methods ---------------------------------------------------------- def focusInEvent(self, event): From 15c9dbb6b750ffec984eb6948894731b07c26027 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Sat, 18 Feb 2017 10:49:51 -0500 Subject: [PATCH 06/13] Improvements in the way to restart (don't shutdowns explicitly the kernel). --- spyder/widgets/ipythonconsole/client.py | 82 +++++++++++++++------- spyder/widgets/ipythonconsole/debugging.py | 5 +- spyder/widgets/ipythonconsole/shell.py | 14 ++-- 3 files changed, 63 insertions(+), 38 deletions(-) diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py index 0601f0878bf..8e6c6bd6416 100644 --- a/spyder/widgets/ipythonconsole/client.py +++ b/spyder/widgets/ipythonconsole/client.py @@ -176,6 +176,9 @@ def configure_shellwidget(self, give_focus=True): # To show kernel restarted/died messages self.shellwidget.sig_kernel_restarted.connect( self.kernel_restarted_message) + + self.shellwidget.sig_debug_restart.connect( + self.restart_kernel) def enable_stop_button(self): self.stop_button.setEnabled(True) @@ -316,42 +319,71 @@ def interrupt_kernel(self): self.shellwidget.request_interrupt_kernel() @Slot() - def restart_kernel(self, force=False): + def restart_kernel(self): """ Restart the associanted kernel Took this code from the qtconsole project Licensed under the BSD license """ - message = _('Are you sure you want to restart the kernel?') - buttons = QMessageBox.Yes | QMessageBox.No - result = QMessageBox.question(self, _('Restart kernel?'), - message, buttons) - if result == QMessageBox.Yes or force: - sw = self.shellwidget + sw = self.shellwidget + # The lines below are needed to restart the kernel without a prompt + # when an error in stdout corrupts the debugging process. + # See issue 4003 + if sw._input_none: if sw.kernel_manager: - if self.infowidget.isVisible(): - self.infowidget.hide() - sw.show() - try: - sw.kernel_manager.restart_kernel() - except RuntimeError as e: + if self.infowidget.isVisible(): + self.infowidget.hide() + sw.show() + try: + sw.kernel_manager.restart_kernel() + except RuntimeError as e: + sw._append_plain_text( + _('Error restarting kernel: %s\n') % e, + before_prompt=True + ) + else: + sw.reset(clear=False) + sw._append_html(_("
Kernel restarting because an " + "error raised while debugging " + "(stdout change)\n

"), + before_prompt=False) + sw._input_none = False + else: + message = _('Are you sure you want to restart the kernel?') + buttons = QMessageBox.Yes | QMessageBox.No + result = QMessageBox.question(self, _('Restart kernel?'), + message, buttons) + if result == QMessageBox.Yes: + if sw.kernel_manager: + if self.infowidget.isVisible(): + self.infowidget.hide() + sw.show() + try: + sw.kernel_manager.restart_kernel() + except RuntimeError as e: + sw._append_plain_text( + _('Error restarting kernel: %s\n') % e, + before_prompt=True + ) + else: + sw.reset(clear=True) + if sw._input_none: + sw._append_html(_("
Kernel restarting because an " + "error raised while debugging " + "(stdout change)\n

"), + before_prompt=False) + sw._input_none = False + else: + sw._append_html(_("
Restarting kernel...\n

"), + before_prompt=False) + else: sw._append_plain_text( - _('Error restarting kernel: %s\n') % e, + _('Cannot restart a kernel not started by Spyder\n'), before_prompt=True ) - else: - sw.reset(clear=True) - sw._append_html(_("
Restarting kernel...\n

"), - before_prompt=False, - ) - else: - sw._append_plain_text( - _('Cannot restart a kernel not started by Spyder\n'), - before_prompt=True - ) - @Slot(str, bool) + @Slot(str) def kernel_restarted_message(self, msg): """Show kernel restarted/died messages.""" stderr = codecs.open(self.stderr_file, 'r', encoding='utf-8').read() diff --git a/spyder/widgets/ipythonconsole/debugging.py b/spyder/widgets/ipythonconsole/debugging.py index b39a7a27f3f..beb253d5b04 100644 --- a/spyder/widgets/ipythonconsole/debugging.py +++ b/spyder/widgets/ipythonconsole/debugging.py @@ -25,7 +25,7 @@ class DebuggingWidget(RichJupyterWidget): Spyder """ - _input_reply = None + _input_reply = {} _input_none = False # --- Public API -------------------------------------------------- @@ -67,10 +67,9 @@ def silent_exec_input(self, code): self.sig_var_properties.emit(properties) else: self.kernel_client.iopub_channel.flush() - self.write_to_stdin('exit') self._input_reply = {} self._input_none = True - self.kernel_client.shutdown() + self.sig_debug_restart.emit() def write_to_stdin(self, line): """Send raw characters to the IPython kernel through stdin""" diff --git a/spyder/widgets/ipythonconsole/shell.py b/spyder/widgets/ipythonconsole/shell.py index 4d850ab979f..4363be6405c 100644 --- a/spyder/widgets/ipythonconsole/shell.py +++ b/spyder/widgets/ipythonconsole/shell.py @@ -40,12 +40,13 @@ class ShellWidget(NamepaceBrowserWidget, HelpWidget, DebuggingWidget): sig_input_reply = Signal() sig_pdb_step = Signal(str, int) sig_prompt_ready = Signal() + sig_debug_restart = Signal() # For ShellWidget focus_changed = Signal() new_client = Signal() sig_got_reply = Signal() - sig_kernel_restarted = Signal(str, bool) + sig_kernel_restarted = Signal(str) def __init__(self, ipyclient, additional_options, interpreter_versions, external_kernel, *args, **kw): @@ -282,15 +283,8 @@ def _banner_default(self): return self.short_banner() def _kernel_restarted_message(self, died=True): - if self._input_none: - msg = _("Kernel restarting because an error raised " - "while debugging (stdout change)") - elif died: - msg = _("Kernel died, restarting") - else: - msg = _("Kernel restarting") - self.sig_kernel_restarted.emit(msg, self._input_none) - self._input_none = False + msg = _("Kernel died, restarting") if died else _("Kernel restarting") + self.sig_kernel_restarted.emit(msg) #---- Qt methods ---------------------------------------------------------- def focusInEvent(self, event): From c4e687a356bd0271cd094160ee127a51545b5984 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 23 Feb 2017 16:20:58 -0500 Subject: [PATCH 07/13] Redo some changes. Changes in names and code style issues. --- spyder/widgets/ipythonconsole/client.py | 45 +++++++++------------- spyder/widgets/ipythonconsole/debugging.py | 4 +- spyder/widgets/ipythonconsole/shell.py | 4 +- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py index 8e6c6bd6416..69b8a8f86d5 100644 --- a/spyder/widgets/ipythonconsole/client.py +++ b/spyder/widgets/ipythonconsole/client.py @@ -174,7 +174,7 @@ def configure_shellwidget(self, give_focus=True): self.shellwidget.executed.connect(self.disable_stop_button) # To show kernel restarted/died messages - self.shellwidget.sig_kernel_restarted.connect( + self.shellwidget.sig_dbg_kernel_restart.connect( self.kernel_restarted_message) self.shellwidget.sig_debug_restart.connect( @@ -330,25 +330,25 @@ def restart_kernel(self): # The lines below are needed to restart the kernel without a prompt # when an error in stdout corrupts the debugging process. # See issue 4003 - if sw._input_none: + if sw._input_reply_failed: if sw.kernel_manager: - if self.infowidget.isVisible(): - self.infowidget.hide() - sw.show() - try: - sw.kernel_manager.restart_kernel() - except RuntimeError as e: - sw._append_plain_text( + if self.infowidget.isVisible(): + self.infowidget.hide() + sw.show() + try: + sw.kernel_manager.restart_kernel() + except RuntimeError as e: + sw._append_plain_text( _('Error restarting kernel: %s\n') % e, before_prompt=True - ) - else: - sw.reset(clear=False) - sw._append_html(_("
Kernel restarting because an " - "error raised while debugging " - "(stdout change)\n

"), - before_prompt=False) - sw._input_none = False + ) + else: + sw.reset(clear=False) + sw._append_html(_("
Restarting kernel because " + "an error occurred while " + "debugging\n

"), + before_prompt=False) + sw._input_reply_failed = False else: message = _('Are you sure you want to restart the kernel?') buttons = QMessageBox.Yes | QMessageBox.No @@ -368,15 +368,8 @@ def restart_kernel(self): ) else: sw.reset(clear=True) - if sw._input_none: - sw._append_html(_("
Kernel restarting because an " - "error raised while debugging " - "(stdout change)\n

"), - before_prompt=False) - sw._input_none = False - else: - sw._append_html(_("
Restarting kernel...\n

"), - before_prompt=False) + sw._append_html(_("
Restarting kernel...\n

"), + before_prompt=False) else: sw._append_plain_text( _('Cannot restart a kernel not started by Spyder\n'), diff --git a/spyder/widgets/ipythonconsole/debugging.py b/spyder/widgets/ipythonconsole/debugging.py index 93c3ccfa1ea..50d3499f08d 100644 --- a/spyder/widgets/ipythonconsole/debugging.py +++ b/spyder/widgets/ipythonconsole/debugging.py @@ -26,7 +26,7 @@ class DebuggingWidget(RichJupyterWidget): """ _input_reply = {} - _input_none = False + _input_reply_failed = False # --- Public API -------------------------------------------------- def silent_exec_input(self, code): @@ -68,7 +68,7 @@ def silent_exec_input(self, code): else: self.kernel_client.iopub_channel.flush() self._input_reply = {} - self._input_none = True + self._input_reply_failed = True self.sig_debug_restart.emit() def write_to_stdin(self, line): diff --git a/spyder/widgets/ipythonconsole/shell.py b/spyder/widgets/ipythonconsole/shell.py index 4363be6405c..5d7c1c6a8c8 100644 --- a/spyder/widgets/ipythonconsole/shell.py +++ b/spyder/widgets/ipythonconsole/shell.py @@ -46,7 +46,7 @@ class ShellWidget(NamepaceBrowserWidget, HelpWidget, DebuggingWidget): focus_changed = Signal() new_client = Signal() sig_got_reply = Signal() - sig_kernel_restarted = Signal(str) + sig_dbg_kernel_restart = Signal(str) def __init__(self, ipyclient, additional_options, interpreter_versions, external_kernel, *args, **kw): @@ -284,7 +284,7 @@ def _banner_default(self): def _kernel_restarted_message(self, died=True): msg = _("Kernel died, restarting") if died else _("Kernel restarting") - self.sig_kernel_restarted.emit(msg) + self.sig_dbg_kernel_restart.emit(msg) #---- Qt methods ---------------------------------------------------------- def focusInEvent(self, event): From fe0292bbabee482eea1bc642c882f22bfec05b56 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 23 Feb 2017 16:59:39 -0500 Subject: [PATCH 08/13] Fixes errors in the changes. --- spyder/widgets/ipythonconsole/client.py | 4 ++-- spyder/widgets/ipythonconsole/debugging.py | 2 +- spyder/widgets/ipythonconsole/shell.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py index 69b8a8f86d5..b4dc782abb0 100644 --- a/spyder/widgets/ipythonconsole/client.py +++ b/spyder/widgets/ipythonconsole/client.py @@ -174,10 +174,10 @@ def configure_shellwidget(self, give_focus=True): self.shellwidget.executed.connect(self.disable_stop_button) # To show kernel restarted/died messages - self.shellwidget.sig_dbg_kernel_restart.connect( + self.shellwidget.sig_kernel_restarted.connect( self.kernel_restarted_message) - self.shellwidget.sig_debug_restart.connect( + self.shellwidget.sig_dbg_kernel_restart.connect( self.restart_kernel) def enable_stop_button(self): diff --git a/spyder/widgets/ipythonconsole/debugging.py b/spyder/widgets/ipythonconsole/debugging.py index 50d3499f08d..6ccd5690e05 100644 --- a/spyder/widgets/ipythonconsole/debugging.py +++ b/spyder/widgets/ipythonconsole/debugging.py @@ -69,7 +69,7 @@ def silent_exec_input(self, code): self.kernel_client.iopub_channel.flush() self._input_reply = {} self._input_reply_failed = True - self.sig_debug_restart.emit() + self.sig_dbg_kernel_restart.emit() def write_to_stdin(self, line): """Send raw characters to the IPython kernel through stdin""" diff --git a/spyder/widgets/ipythonconsole/shell.py b/spyder/widgets/ipythonconsole/shell.py index 5d7c1c6a8c8..5367a9c3d45 100644 --- a/spyder/widgets/ipythonconsole/shell.py +++ b/spyder/widgets/ipythonconsole/shell.py @@ -40,13 +40,13 @@ class ShellWidget(NamepaceBrowserWidget, HelpWidget, DebuggingWidget): sig_input_reply = Signal() sig_pdb_step = Signal(str, int) sig_prompt_ready = Signal() - sig_debug_restart = Signal() + sig_dbg_kernel_restart = Signal() # For ShellWidget focus_changed = Signal() new_client = Signal() sig_got_reply = Signal() - sig_dbg_kernel_restart = Signal(str) + sig_kernel_restarted = Signal(str) def __init__(self, ipyclient, additional_options, interpreter_versions, external_kernel, *args, **kw): From bc361dc649b5f6ada72f006ffbf557652767d11b Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 24 Feb 2017 10:52:02 -0500 Subject: [PATCH 09/13] Reverts unrelated change. Adds comment. --- spyder/widgets/ipythonconsole/client.py | 4 +++- spyder/widgets/ipythonconsole/shell.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py index b4dc782abb0..8e1b0b1f7cd 100644 --- a/spyder/widgets/ipythonconsole/client.py +++ b/spyder/widgets/ipythonconsole/client.py @@ -176,7 +176,9 @@ def configure_shellwidget(self, give_focus=True): # To show kernel restarted/died messages self.shellwidget.sig_kernel_restarted.connect( self.kernel_restarted_message) - + + # To restart the kernel when errors happened while debugging + # See issue 4003 self.shellwidget.sig_dbg_kernel_restart.connect( self.restart_kernel) diff --git a/spyder/widgets/ipythonconsole/shell.py b/spyder/widgets/ipythonconsole/shell.py index 5367a9c3d45..e8d3bddca1f 100644 --- a/spyder/widgets/ipythonconsole/shell.py +++ b/spyder/widgets/ipythonconsole/shell.py @@ -284,7 +284,7 @@ def _banner_default(self): def _kernel_restarted_message(self, died=True): msg = _("Kernel died, restarting") if died else _("Kernel restarting") - self.sig_dbg_kernel_restart.emit(msg) + self.sig_kernel_restarted.emit(msg) #---- Qt methods ---------------------------------------------------------- def focusInEvent(self, event): From 102d63d14014423203f31cd60d232bc4f3a4f377 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 24 Feb 2017 17:55:03 -0500 Subject: [PATCH 10/13] Simplify restart_kernel code --- spyder/widgets/ipythonconsole/client.py | 62 +++++++++++-------------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py index 8e1b0b1f7cd..151d53c813e 100644 --- a/spyder/widgets/ipythonconsole/client.py +++ b/spyder/widgets/ipythonconsole/client.py @@ -329,10 +329,19 @@ def restart_kernel(self): Licensed under the BSD license """ sw = self.shellwidget - # The lines below are needed to restart the kernel without a prompt + + # This is needed to restart the kernel without a prompt # when an error in stdout corrupts the debugging process. # See issue 4003 - if sw._input_reply_failed: + if not sw._input_reply_failed: + message = _('Are you sure you want to restart the kernel?') + buttons = QMessageBox.Yes | QMessageBox.No + result = QMessageBox.question(self, _('Restart kernel?'), + message, buttons) + else: + result = None + + if result == QMessageBox.Yes or sw._input_reply_failed: if sw.kernel_manager: if self.infowidget.isVisible(): self.infowidget.hide() @@ -341,42 +350,25 @@ def restart_kernel(self): sw.kernel_manager.restart_kernel() except RuntimeError as e: sw._append_plain_text( - _('Error restarting kernel: %s\n') % e, - before_prompt=True - ) + _('Error restarting kernel: %s\n') % e, + before_prompt=True + ) else: - sw.reset(clear=False) - sw._append_html(_("
Restarting kernel because " - "an error occurred while " - "debugging\n

"), - before_prompt=False) - sw._input_reply_failed = False - else: - message = _('Are you sure you want to restart the kernel?') - buttons = QMessageBox.Yes | QMessageBox.No - result = QMessageBox.question(self, _('Restart kernel?'), - message, buttons) - if result == QMessageBox.Yes: - if sw.kernel_manager: - if self.infowidget.isVisible(): - self.infowidget.hide() - sw.show() - try: - sw.kernel_manager.restart_kernel() - except RuntimeError as e: - sw._append_plain_text( - _('Error restarting kernel: %s\n') % e, - before_prompt=True - ) + sw.reset(clear=not sw._input_reply_failed) + if sw._input_reply_failed: + sw._append_html(_("
Restarting kernel because " + "an error occurred while " + "debugging\n

"), + before_prompt=False) + sw._input_reply_failed = False else: - sw.reset(clear=True) sw._append_html(_("
Restarting kernel...\n

"), - before_prompt=False) - else: - sw._append_plain_text( - _('Cannot restart a kernel not started by Spyder\n'), - before_prompt=True - ) + before_prompt=False) + else: + sw._append_plain_text( + _('Cannot restart a kernel not started by Spyder\n'), + before_prompt=True + ) @Slot(str) def kernel_restarted_message(self, msg): From dc0ad9d30305965cf1e8a04148e7b7507c2124ff Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 24 Feb 2017 18:28:47 -0500 Subject: [PATCH 11/13] Testing: Add a test to verify that kernels are restarted correctly --- spyder/plugins/tests/test_ipythonconsole.py | 34 +++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/spyder/plugins/tests/test_ipythonconsole.py b/spyder/plugins/tests/test_ipythonconsole.py index 2bad799d177..4b5014694b3 100644 --- a/spyder/plugins/tests/test_ipythonconsole.py +++ b/spyder/plugins/tests/test_ipythonconsole.py @@ -12,7 +12,7 @@ from flaky import flaky import pytest from qtpy.QtCore import Qt, QTimer -from qtpy.QtWidgets import QApplication +from qtpy.QtWidgets import QApplication, QMessageBox from spyder.plugins.ipythonconsole import (IPythonConsole, KernelConnectionDialog) @@ -35,6 +35,13 @@ def open_client_from_connection_info(connection_info, qtbot): qtbot.keyClick(w, Qt.Key_Enter) +def restart_kernel(qtbot): + top_level_widgets = QApplication.topLevelWidgets() + for w in top_level_widgets: + if isinstance(w, QMessageBox): + qtbot.keyClick(w, Qt.Key_Enter) + + #============================================================================== # Qt Test Fixtures #============================================================================== @@ -52,7 +59,30 @@ def close_widget(): #============================================================================== # Tests #============================================================================== -@flaky(max_runs=3) +@flaky(max_runs=10) +@pytest.mark.skipif(os.name == 'nt', reason="It times out on Windows") +def test_restart_kernel(ipyconsole, qtbot): + """ + Test that kernel is restarted correctly + """ + shell = ipyconsole.get_current_shellwidget() + client = ipyconsole.get_current_client() + qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT) + + # Do an assigment to verify that it's not there after restarting + shell.execute('a = 10') + qtbot.wait(500) + + # Restart kernel and wait until it's up again + shell._prompt_html = None + QTimer.singleShot(1000, lambda: restart_kernel(qtbot)) + client.restart_kernel() + qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT) + + assert not shell.is_defined('a') + + +@flaky(max_runs=10) @pytest.mark.skipif(os.name == 'nt', reason="It times out on Windows") def test_load_kernel_file_from_id(ipyconsole, qtbot): """ From d531429192a3fb541f416c2c96418302f67ce618 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 24 Feb 2017 19:00:56 -0500 Subject: [PATCH 12/13] Testing: Verify that forced kernel restarts are working while debugging --- spyder/plugins/tests/test_ipythonconsole.py | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/spyder/plugins/tests/test_ipythonconsole.py b/spyder/plugins/tests/test_ipythonconsole.py index 4b5014694b3..c7a56120beb 100644 --- a/spyder/plugins/tests/test_ipythonconsole.py +++ b/spyder/plugins/tests/test_ipythonconsole.py @@ -59,6 +59,35 @@ def close_widget(): #============================================================================== # Tests #============================================================================== +#@flaky(max_runs=10) +@pytest.mark.skipif(os.name == 'nt', reason="It times out on Windows") +def test_forced_restart_kernel(ipyconsole, qtbot): + """ + Test that kernel is restarted if we force it do it + during debugging + """ + shell = ipyconsole.get_current_shellwidget() + client = ipyconsole.get_current_client() + qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT) + + # Do an assigment to verify that it's not there after restarting + shell.execute('a = 10') + qtbot.wait(500) + + # Generate a traceback and enter debugging mode + shell.execute('1/0') + qtbot.wait(500) + shell.execute('%debug') + qtbot.wait(500) + + # Force a kernel restart and wait until it's up again + shell._prompt_html = None + shell.silent_exec_input("1+1") # The return type of this must be a dict!! + qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT) + + assert not shell.is_defined('a') + + @flaky(max_runs=10) @pytest.mark.skipif(os.name == 'nt', reason="It times out on Windows") def test_restart_kernel(ipyconsole, qtbot): From 240edccc443c3bde58c5f67d413ad562a6d0e3f0 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 24 Feb 2017 19:01:46 -0500 Subject: [PATCH 13/13] Testing: Skip a couple of tests locally because they fail sometimes --- spyder/plugins/tests/test_ipythonconsole.py | 2 +- spyder/utils/tests/test_programs.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spyder/plugins/tests/test_ipythonconsole.py b/spyder/plugins/tests/test_ipythonconsole.py index c7a56120beb..c7a07db5288 100644 --- a/spyder/plugins/tests/test_ipythonconsole.py +++ b/spyder/plugins/tests/test_ipythonconsole.py @@ -59,7 +59,7 @@ def close_widget(): #============================================================================== # Tests #============================================================================== -#@flaky(max_runs=10) +@flaky(max_runs=10) @pytest.mark.skipif(os.name == 'nt', reason="It times out on Windows") def test_forced_restart_kernel(ipyconsole, qtbot): """ diff --git a/spyder/utils/tests/test_programs.py b/spyder/utils/tests/test_programs.py index bc904d503d3..cc88fa9227a 100644 --- a/spyder/utils/tests/test_programs.py +++ b/spyder/utils/tests/test_programs.py @@ -23,7 +23,8 @@ INVALID_INTERPRETER = os.path.join(home_dir, 'miniconda', 'bin', 'ipython') -@pytest.mark.skipif(os.name == 'nt', reason='gets stuck on Windows') # FIXME +@pytest.mark.skipif(os.name == 'nt' or os.environ.get('CI', None) is None, + reason='gets stuck on Windows and fails sometimes locally') # FIXME def test_run_python_script_in_terminal(tmpdir, qtbot): scriptpath = tmpdir.join('write-done.py') outfilepath = tmpdir.join('out.txt') @@ -37,7 +38,8 @@ def test_run_python_script_in_terminal(tmpdir, qtbot): assert res == 'done' -@pytest.mark.skipif(os.name == 'nt', reason='gets stuck on Windows') # FIXME +@pytest.mark.skipif(os.name == 'nt' or os.environ.get('CI', None) is None, + reason='gets stuck on Windows and fails sometimes locally') # FIXME def test_run_python_script_in_terminal_with_wdir_empty(tmpdir, qtbot): scriptpath = tmpdir.join('write-done.py') outfilepath = tmpdir.join('out.txt')