diff --git a/spyder/app/mainwindow.py b/spyder/app/mainwindow.py
index ad1fccf550f..78df4523a5e 100644
--- a/spyder/app/mainwindow.py
+++ b/spyder/app/mainwindow.py
@@ -301,6 +301,10 @@ def signal_handler(signum, frame=None):
mac_style = mac_style.replace('$IMAGE_PATH', img_path)
self.setStyleSheet(mac_style)
+ # Create our TEMPDIR
+ if not osp.isdir(programs.TEMPDIR):
+ os.mkdir(programs.TEMPDIR)
+
# Shortcut management data
self.shortcut_data = []
diff --git a/spyder/config/main.py b/spyder/config/main.py
index d55f8269138..1cf7e2161a7 100755
--- a/spyder/config/main.py
+++ b/spyder/config/main.py
@@ -438,6 +438,7 @@
# ---- In widgets/ipythonconsole/shell.py ----
'ipython_console/new tab': "Ctrl+T",
'ipython_console/reset namespace': "Ctrl+Alt+R",
+ 'ipython_console/restart kernel': "Ctrl+.",
# ---- In widgets/arraybuider.py ----
'array_builder/enter array inline': "Ctrl+Alt+M",
'array_builder/enter array table': "Ctrl+M",
diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py
index b216404d049..01ed8a9a52c 100644
--- a/spyder/plugins/ipythonconsole.py
+++ b/spyder/plugins/ipythonconsole.py
@@ -15,6 +15,7 @@
# Standard library imports
import atexit
+import codecs
import os
import os.path as osp
import uuid
@@ -724,20 +725,22 @@ def refresh_plugin(self):
self.sig_update_plugin_title.emit()
def get_plugin_actions(self):
- """Return a list of actions related to plugin"""
- ctrl = "Cmd" if sys.platform == "darwin" else "Ctrl"
- main_create_client_action = create_action(self,
- _("Open an &IPython console"),
- None, ima.icon('ipython_console'),
- triggered=self.create_new_client,
- tip=_("Use %s+T when the console is selected "
- "to open a new one") % ctrl)
- create_client_action = create_action(self,
- _("Open a new console"),
- QKeySequence("Ctrl+T"),
- ima.icon('ipython_console'),
- triggered=self.create_new_client,
- context=Qt.WidgetWithChildrenShortcut)
+ """Return a list of actions related to plugin."""
+ create_client_action = create_action(
+ self,
+ _("Open an &IPython console"),
+ icon=ima.icon('ipython_console'),
+ triggered=self.create_new_client,
+ context=Qt.WidgetWithChildrenShortcut)
+ self.register_shortcut(create_client_action, context="ipython_console",
+ name="New tab")
+
+ restart_action = create_action(self, _("Restart kernel"),
+ icon=ima.icon('restart'),
+ triggered=self.restart_kernel,
+ context=Qt.WidgetWithChildrenShortcut)
+ self.register_shortcut(restart_action, context="ipython_console",
+ name="Restart kernel")
connect_to_kernel_action = create_action(self,
_("Connect to an existing kernel"), None, None,
@@ -746,11 +749,13 @@ def get_plugin_actions(self):
# Add the action to the 'Consoles' menu on the main window
main_consoles_menu = self.main.consoles_menu_actions
- main_consoles_menu.insert(0, main_create_client_action)
- main_consoles_menu += [MENU_SEPARATOR, connect_to_kernel_action]
+ main_consoles_menu.insert(0, create_client_action)
+ main_consoles_menu += [MENU_SEPARATOR, restart_action,
+ connect_to_kernel_action]
# Plugin actions
- self.menu_actions = [create_client_action, connect_to_kernel_action]
+ self.menu_actions = [restart_action, MENU_SEPARATOR,
+ create_client_action, connect_to_kernel_action]
return self.menu_actions
@@ -911,7 +916,9 @@ def create_client_for_kernel(self):
def connect_client_to_kernel(self, client):
"""Connect a client to its kernel"""
connection_file = client.connection_file
- km, kc = self.create_kernel_manager_and_kernel_client(connection_file)
+ stderr_file = client.stderr_file
+ km, kc = self.create_kernel_manager_and_kernel_client(connection_file,
+ stderr_file)
kc.started_channels.connect(lambda c=client: self.process_started(c))
kc.stopped_channels.connect(lambda c=client: self.process_finished(c))
@@ -1290,13 +1297,17 @@ def create_kernel_spec(self):
return KernelSpec(resource_dir='', **kernel_dict)
- def create_kernel_manager_and_kernel_client(self, connection_file):
- """Create kernel manager and client"""
+ def create_kernel_manager_and_kernel_client(self, connection_file,
+ stderr_file):
+ """Create kernel manager and client."""
# Kernel manager
kernel_manager = QtKernelManager(connection_file=connection_file,
config=None, autorestart=True)
kernel_manager._kernel_spec = self.create_kernel_spec()
- kernel_manager.start_kernel()
+
+ # Save stderr in a file to read it later in case of errors
+ stderr = codecs.open(stderr_file, 'w', encoding='utf-8')
+ kernel_manager.start_kernel(stderr=stderr)
# Kernel client
kernel_client = kernel_manager.client()
@@ -1307,6 +1318,12 @@ def create_kernel_manager_and_kernel_client(self, connection_file):
return kernel_manager, kernel_client
+ def restart_kernel(self):
+ """Restart kernel of current client."""
+ client = self.get_current_client()
+ if client is not None:
+ client.restart_kernel()
+
#------ Public API (for tabs) ---------------------------------------------
def add_tab(self, widget, name):
"""Add tab"""
diff --git a/spyder/utils/programs.py b/spyder/utils/programs.py
index 41e3dd39ebd..cb758078a21 100644
--- a/spyder/utils/programs.py
+++ b/spyder/utils/programs.py
@@ -396,9 +396,6 @@ def is_module_installed(module_name, version=None, installed_version=None,
in a determined interpreter
"""
if interpreter:
- if not osp.isdir(TEMPDIR):
- os.mkdir(TEMPDIR)
-
if osp.isfile(interpreter) and ('python' in interpreter):
checkver = inspect.getsource(check_version)
get_modver = inspect.getsource(get_module_version)
diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py
index fbedfcbbfa5..39d6d06d662 100644
--- a/spyder/widgets/ipythonconsole/client.py
+++ b/spyder/widgets/ipythonconsole/client.py
@@ -13,6 +13,7 @@
# Standard library imports
from __future__ import absolute_import # Fix for Issue 1356
+import codecs
import os
import os.path as osp
from string import Template
@@ -30,6 +31,8 @@
get_module_source_path)
from spyder.config.gui import get_font, get_shortcut
from spyder.utils import icon_manager as ima
+from spyder.utils import sourcecode
+from spyder.utils.programs import TEMPDIR
from spyder.utils.qthelpers import (add_actions, create_action,
create_toolbutton)
from spyder.widgets.browser import WebView
@@ -103,16 +106,15 @@ def __init__(self, plugin, name, history_filename, config_options,
# --- Widgets
self.shellwidget = ShellWidget(config=config_options,
+ ipyclient=self,
additional_options=additional_options,
interpreter_versions=interpreter_versions,
external_kernel=external_kernel,
local_kernel=True)
- self.shellwidget.hide()
self.infowidget = WebView(self)
self.set_infowidget_font()
self.loading_page = self._create_loading_page()
- self.infowidget.setHtml(self.loading_page,
- QUrl.fromLocalFile(CSS_PATH))
+ self._show_loading_page()
# --- Layout
vlayout = QVBoxLayout()
@@ -133,16 +135,24 @@ def __init__(self, plugin, name, history_filename, config_options,
# As soon as some content is printed in the console, stop
# our loading animation
document = self.get_control().document()
- document.contentsChange.connect(self._stop_loading_animation)
+ document.contentsChange.connect(self._hide_loading_page)
#------ Public API --------------------------------------------------------
+ @property
+ def stderr_file(self):
+ """Filename to save kernel stderr output."""
+ json_file = osp.basename(self.connection_file)
+ stderr_file = json_file.split('json')[0] + 'stderr'
+ stderr_file = osp.join(TEMPDIR, stderr_file)
+ return stderr_file
+
def configure_shellwidget(self, give_focus=True):
"""Configure shellwidget after kernel is started"""
if give_focus:
self.get_control().setFocus()
- # Connect shellwidget to the client
- self.shellwidget.set_ipyclient(self)
+ # Set exit callback
+ self.shellwidget.set_exit_callback()
# To save history
self.shellwidget.executing.connect(self.add_to_history)
@@ -163,6 +173,10 @@ def configure_shellwidget(self, give_focus=True):
# To disable the stop button after execution stopped
self.shellwidget.executed.connect(self.disable_stop_button)
+ # To show kernel restarted/died messages
+ self.shellwidget.sig_kernel_restarted.connect(
+ self.kernel_restarted_message)
+
def enable_stop_button(self):
self.stop_button.setEnabled(True)
@@ -180,7 +194,12 @@ def stop_button_click_handler(self):
self.shellwidget.write_to_stdin('exit')
def show_kernel_error(self, error):
- """Show kernel initialization errors in infowidget"""
+ """Show kernel initialization errors in infowidget."""
+ # Replace end of line chars with
+ eol = sourcecode.get_eol_chars(error)
+ if eol:
+ error = error.replace(eol, '
')
+
# Don't break lines in hyphens
# From http://stackoverflow.com/q/7691569/438386
error = error.replace('-', '‑')
@@ -217,27 +236,18 @@ def get_kernel(self):
def get_options_menu(self):
"""Return options menu"""
- restart_action = create_action(self, _("Restart kernel"),
- shortcut=QKeySequence("Ctrl+."),
- icon=ima.icon('restart'),
- triggered=self.restart_kernel,
- context=Qt.WidgetWithChildrenShortcut)
-
- # Main menu
- if self.menu_actions is not None:
- actions = [restart_action, None] + self.menu_actions
- else:
- actions = [restart_action]
- return actions
+ return self.menu_actions
def get_toolbar_buttons(self):
- """Return toolbar buttons list"""
+ """Return toolbar buttons list."""
buttons = []
# Code to add the stop button
if self.stop_button is None:
- self.stop_button = create_toolbutton(self, text=_("Stop"),
- icon=self.stop_icon,
- tip=_("Stop the current command"))
+ self.stop_button = create_toolbutton(
+ self,
+ text=_("Stop"),
+ icon=self.stop_icon,
+ tip=_("Stop the current command"))
self.disable_stop_button()
# set click event handler
self.stop_button.clicked.connect(self.stop_button_click_handler)
@@ -320,6 +330,9 @@ def restart_kernel(self):
if result == QMessageBox.Yes:
sw = self.shellwidget
if sw.kernel_manager:
+ if self.infowidget.isVisible():
+ self.infowidget.hide()
+ sw.show()
try:
sw.kernel_manager.restart_kernel()
except RuntimeError as e:
@@ -328,8 +341,9 @@ def restart_kernel(self):
before_prompt=True
)
else:
+ sw.reset(clear=True)
sw._append_html(_("
Restarting kernel...\n