diff --git a/spyder/app/mainwindow.py b/spyder/app/mainwindow.py
index ac31abbabca..5d45a7bff5b 100644
--- a/spyder/app/mainwindow.py
+++ b/spyder/app/mainwindow.py
@@ -2107,7 +2107,8 @@ def set_splash(self, message):
def remove_tmpdir(self):
"""Remove Spyder temporary directory"""
- shutil.rmtree(programs.TEMPDIR, ignore_errors=True)
+ if CONF.get('main', 'single_instance') and not self.new_instance:
+ shutil.rmtree(programs.TEMPDIR, ignore_errors=True)
def closeEvent(self, event):
"""closeEvent reimplementation"""
@@ -2433,8 +2434,8 @@ def open_external_console(self, fname, wdir, args, interact, debug, python,
def execute_in_external_console(self, lines, focus_to_editor):
"""
- Execute lines in external or IPython console and eventually set focus
- to the editor
+ Execute lines in IPython console and eventually set focus
+ to the Editor.
"""
console = self.ipyconsole
console.visibility_changed(True)
diff --git a/spyder/plugins/editor.py b/spyder/plugins/editor.py
index a856f768899..c15b793f40e 100644
--- a/spyder/plugins/editor.py
+++ b/spyder/plugins/editor.py
@@ -369,7 +369,7 @@ class Editor(SpyderPluginWidget):
DISABLE_ACTIONS_WHEN_HIDDEN = False # SpyderPluginWidget class attribute
# Signals
- run_in_current_ipyclient = Signal(str, str, str, bool, bool, bool)
+ run_in_current_ipyclient = Signal(str, str, str, bool, bool, bool, bool)
exec_in_extconsole = Signal(str, bool)
redirect_stdio = Signal(bool)
open_dir = Signal(str)
@@ -2408,10 +2408,10 @@ def re_run_file(self):
(fname, wdir, args, interact, debug,
python, python_args, current, systerm,
post_mortem, clear_namespace) = self.__last_ec_exec
- if current:
+ if not systerm:
self.run_in_current_ipyclient.emit(fname, wdir, args,
debug, post_mortem,
- clear_namespace)
+ current, clear_namespace)
else:
self.main.open_external_console(fname, wdir, args, interact,
debug, python, python_args,
diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py
index c8701b69bd2..03ab6b5d456 100644
--- a/spyder/plugins/ipythonconsole.py
+++ b/spyder/plugins/ipythonconsole.py
@@ -621,7 +621,8 @@ def __init__(self, parent, testing=False):
os.mkdir(programs.TEMPDIR)
layout = QVBoxLayout()
- self.tabwidget = Tabs(self, self.menu_actions, rename_tabs=True)
+ self.tabwidget = Tabs(self, self.menu_actions, rename_tabs=True,
+ split_char='/', split_index=0)
if hasattr(self.tabwidget, 'setDocumentMode')\
and not sys.platform == 'darwin':
# Don't set document mode to true on OSX because it generates
@@ -799,8 +800,7 @@ def register_plugin(self):
self.editor.load(fname, lineno, word,
processevents=processevents))
self.editor.breakpoints_saved.connect(self.set_spyder_breakpoints)
- self.editor.run_in_current_ipyclient.connect(
- self.run_script_in_current_client)
+ self.editor.run_in_current_ipyclient.connect(self.run_script)
self.main.workingdirectory.set_current_console_wd.connect(
self.set_current_client_working_directory)
self.explorer.open_interpreter.connect(self.create_client_from_path)
@@ -830,11 +830,20 @@ def get_current_shellwidget(self):
if client is not None:
return client.shellwidget
- def run_script_in_current_client(self, filename, wdir, args, debug,
- post_mortem, clear_variables):
- """Run script in current client, if any"""
+ def run_script(self, filename, wdir, args, debug, post_mortem,
+ current_client, clear_variables):
+ """Run script in current or dedicated client"""
norm = lambda text: remove_backslashes(to_text_string(text))
- client = self.get_current_client()
+
+ # Select client to execute code on it
+ if current_client:
+ client = self.get_current_client()
+ else:
+ client = self.get_client_for_file(filename)
+ if client is None:
+ self.create_client_for_file(filename)
+ client = self.get_current_client()
+
if client is not None:
# Internal kernels, use runfile
if client.get_kernel() is not None:
@@ -854,7 +863,7 @@ def run_script_in_current_client(self, filename, wdir, args, debug,
line += "\"%s\"" % to_text_string(filename)
if args:
line += " %s" % norm(args)
- self.execute_code(line, clear_variables)
+ self.execute_code(line, current_client, clear_variables)
self.visibility_changed(True)
self.raise_()
else:
@@ -871,14 +880,21 @@ def set_current_client_working_directory(self, directory):
directory = encoding.to_unicode_from_fs(directory)
shellwidget.set_cwd(directory)
- def execute_code(self, lines, clear_variables=False):
+ def execute_code(self, lines, current_client=True, clear_variables=False):
"""Execute code instructions."""
sw = self.get_current_shellwidget()
if sw is not None:
if sw._reading:
pass
else:
- if clear_variables:
+ if not current_client:
+ # Clear console and reset namespace for
+ # dedicated clients
+ sw.silent_execute('%clear')
+ sw.silent_execute(
+ 'get_ipython().kernel.close_all_mpl_figures()')
+ sw.reset_namespace(force=True)
+ elif current_client and clear_variables:
sw.reset_namespace(force=True)
sw.execute(to_text_string(to_text_string(lines)))
self.activateWindow()
@@ -891,9 +907,7 @@ def write_to_stdin(self, line):
@Slot()
@Slot(bool)
- @Slot(str)
- @Slot(bool, str)
- def create_new_client(self, give_focus=True, path=''):
+ def create_new_client(self, give_focus=True):
"""Create a new client"""
self.master_clients += 1
client_id = dict(int_id=to_text_string(self.master_clients),
@@ -935,7 +949,7 @@ def create_new_client(self, give_focus=True, path=''):
"conda install ipykernel"))
return
- self.connect_client_to_kernel(client, path)
+ self.connect_client_to_kernel(client)
if client.shellwidget.kernel_manager is None:
return
self.register_client(client)
@@ -951,7 +965,7 @@ def create_client_for_kernel(self):
self._create_client_for_kernel(connection_file, hostname, sshkey,
password)
- def connect_client_to_kernel(self, client, path):
+ def connect_client_to_kernel(self, client):
"""Connect a client to its kernel"""
connection_file = client.connection_file
stderr_file = client.stderr_file
@@ -971,9 +985,6 @@ def connect_client_to_kernel(self, client, path):
shellwidget.kernel_manager = km
shellwidget.kernel_client = kc
- if path:
- shellwidget.set_cwd(path)
-
def set_editor(self):
"""Set the editor used by the %edit magic"""
python = sys.executable
@@ -1246,8 +1257,32 @@ def set_spyder_breakpoints(self):
@Slot(str)
def create_client_from_path(self, path):
- """Create a client with its cwd pointing to path"""
- self.create_new_client(path=path)
+ """Create a client with its cwd pointing to path."""
+ self.create_new_client()
+ sw = self.get_current_shellwidget()
+ sw.set_cwd(path)
+
+ def create_client_for_file(self, filename):
+ """Create a client to execute code related to a file."""
+ # Create client
+ self.create_new_client()
+
+ # Don't increase the count of master clients
+ self.master_clients -= 1
+
+ # Rename client tab with filename
+ client = self.get_current_client()
+ client.allow_rename = False
+ self.rename_client_tab(client, filename)
+
+ def get_client_for_file(self, filename):
+ """Get client associated with a given file."""
+ client = None
+ for cl in self.get_clients():
+ if cl.given_name == filename:
+ client = cl
+ break
+ return client
#------ Public API (for kernels) ------------------------------------------
def ssh_tunnel(self, *args, **kwargs):
@@ -1339,24 +1374,27 @@ def move_tab(self, index_from, index_to):
self.clients.insert(index_to, client)
self.update_plugin_title.emit()
- def rename_client_tab(self, client):
+ def rename_client_tab(self, client, given_name):
"""Rename client's tab"""
index = self.get_client_index_from_id(id(client))
+
+ if given_name is not None:
+ client.given_name = given_name
self.tabwidget.setTabText(index, client.get_name())
def rename_tabs_after_change(self, given_name):
client = self.get_current_client()
# Rename current client tab to add str_id
- if client.allow_rename:
- client.given_name = given_name
- self.rename_client_tab(client)
+ if client.allow_rename and not u'/' in given_name:
+ self.rename_client_tab(client, given_name)
+ else:
+ self.rename_client_tab(client, None)
# Rename related clients
- if client.allow_rename:
+ if client.allow_rename and not u'/' in given_name:
for cl in self.get_related_clients(client):
- cl.given_name = given_name
- self.rename_client_tab(cl)
+ self.rename_client_tab(cl, given_name)
def tab_name_editor(self):
"""Trigger the tab name editor."""
diff --git a/spyder/plugins/runconfig.py b/spyder/plugins/runconfig.py
index 9b208e05437..a108cf22e86 100644
--- a/spyder/plugins/runconfig.py
+++ b/spyder/plugins/runconfig.py
@@ -26,9 +26,9 @@
from spyder.utils import icon_manager as ima
-CURRENT_INTERPRETER = _("Execute in current Python or IPython console")
-DEDICATED_INTERPRETER = _("Execute in a new dedicated Python console")
-SYSTERM_INTERPRETER = _("Execute in an external System terminal")
+CURRENT_INTERPRETER = _("Execute in current console")
+DEDICATED_INTERPRETER = _("Execute in a dedicated console")
+SYSTERM_INTERPRETER = _("Execute in an external system terminal")
CURRENT_INTERPRETER_OPTION = 'default/interpreter/current'
DEDICATED_INTERPRETER_OPTION = 'default/interpreter/dedicated'
@@ -41,11 +41,13 @@
ALWAYS_OPEN_FIRST_RUN = _("Always show %s on a first file run")
ALWAYS_OPEN_FIRST_RUN_OPTION = 'open_on_firstrun'
-CLEAR_ALL_VARIABLES = _("Clear all variables before execution (IPython "
- "consoles)")
+CLEAR_ALL_VARIABLES = _("Clear all variables before execution")
+INTERACT = _("Interact with the Python console after execution")
+
class RunConfiguration(object):
"""Run configuration"""
+
def __init__(self, fname=None):
self.args = None
self.args_enabled = None
@@ -54,12 +56,13 @@ def __init__(self, fname=None):
self.current = None
self.systerm = None
self.interact = None
- self.show_kill_warning =None
self.post_mortem = None
self.python_args = None
self.python_args_enabled = None
self.clear_namespace = None
+
self.set(CONF.get('run', 'defaultconfiguration', default={}))
+
if fname is not None and\
CONF.get('run', WDIR_USE_SCRIPT_DIR_OPTION, True):
self.wdir = osp.dirname(fname)
@@ -81,8 +84,6 @@ def set(self, options):
CONF.get('run', SYSTERM_INTERPRETER_OPTION, False))
self.interact = options.get('interact',
CONF.get('run', 'interact', False))
- self.show_kill_warning = options.get('show_kill_warning',
- CONF.get('run', 'show_kill_warning', False))
self.post_mortem = options.get('post_mortem',
CONF.get('run', 'post_mortem', False))
self.python_args = options.get('python_args', '')
@@ -99,7 +100,6 @@ def get(self):
'current': self.current,
'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,
@@ -124,7 +124,7 @@ def get_python_arguments(self):
else:
return ''
-
+
def _get_run_configurations():
history_count = CONF.get('run', 'history', 20)
try:
@@ -135,10 +135,12 @@ def _get_run_configurations():
CONF.set('run', 'configurations', [])
return []
+
def _set_run_configurations(configurations):
history_count = CONF.get('run', 'history', 20)
CONF.set('run', 'configurations', configurations[:history_count])
-
+
+
def get_run_configuration(fname):
"""Return script *fname* run configuration"""
configurations = _get_run_configurations()
@@ -189,9 +191,7 @@ def __init__(self, parent=None):
self.post_mortem_cb = QCheckBox(_("Enter debugging mode when "
"errors appear during execution"))
common_layout.addWidget(self.post_mortem_cb)
-
-
# --- Interpreter ---
interpreter_group = QGroupBox(_("Console"))
interpreter_layout = QVBoxLayout()
@@ -202,28 +202,25 @@ def __init__(self, parent=None):
interpreter_layout.addWidget(self.dedicated_radio)
self.systerm_radio = QRadioButton(SYSTERM_INTERPRETER)
interpreter_layout.addWidget(self.systerm_radio)
-
- # --- Dedicated interpreter ---
- new_group = QGroupBox(_("Dedicated Python console"))
- self.current_radio.toggled.connect(new_group.setDisabled)
- new_layout = QGridLayout()
- new_group.setLayout(new_layout)
- self.interact_cb = QCheckBox(_("Interact with the Python "
- "console after execution"))
- new_layout.addWidget(self.interact_cb, 1, 0, 1, -1)
-
- self.show_kill_warning_cb = QCheckBox(_("Show warning when killing"
- " running process"))
- new_layout.addWidget(self.show_kill_warning_cb, 2, 0, 1, -1)
+ # --- System terminal ---
+ external_group = QGroupBox(_("External system terminal"))
+ external_group.setDisabled(True)
+ self.systerm_radio.toggled.connect(external_group.setEnabled)
+
+ external_layout = QGridLayout()
+ external_group.setLayout(external_layout)
+ self.interact_cb = QCheckBox(INTERACT)
+ external_layout.addWidget(self.interact_cb, 1, 0, 1, -1)
+
self.pclo_cb = QCheckBox(_("Command line options:"))
- new_layout.addWidget(self.pclo_cb, 3, 0)
+ external_layout.addWidget(self.pclo_cb, 3, 0)
self.pclo_edit = QLineEdit()
self.pclo_cb.toggled.connect(self.pclo_edit.setEnabled)
self.pclo_edit.setEnabled(False)
self.pclo_edit.setToolTip(_("-u is added to the "
"other options you set here"))
- new_layout.addWidget(self.pclo_edit, 3, 1)
+ external_layout.addWidget(self.pclo_edit, 3, 1)
# Checkbox to preserve the old behavior, i.e. always open the dialog
@@ -238,7 +235,7 @@ def __init__(self, parent=None):
layout = QVBoxLayout()
layout.addWidget(interpreter_group)
layout.addWidget(common_group)
- layout.addWidget(new_group)
+ layout.addWidget(external_group)
layout.addWidget(hline)
layout.addWidget(self.firstrun_cb)
self.setLayout(layout)
@@ -266,7 +263,6 @@ def set(self, options):
else:
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)
@@ -280,7 +276,6 @@ def get(self):
self.runconf.current = self.current_radio.isChecked()
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())
@@ -514,18 +509,12 @@ def setup_page(self):
general_layout.addWidget(post_mortem)
general_group.setLayout(general_layout)
- dedicated_group = QGroupBox(_("Dedicated Python console"))
- interact_after = self.create_checkbox(
- _("Interact with the Python console after execution"),
- 'interact', False)
- show_warning = self.create_checkbox(
- _("Show warning when killing running processes"),
- 'show_kill_warning', True)
+ external_group = QGroupBox(_("External system terminal"))
+ interact_after = self.create_checkbox(INTERACT, 'interact', False)
- dedicated_layout = QVBoxLayout()
- dedicated_layout.addWidget(interact_after)
- dedicated_layout.addWidget(show_warning)
- dedicated_group.setLayout(dedicated_layout)
+ external_layout = QVBoxLayout()
+ external_layout.addWidget(interact_after)
+ external_group.setLayout(external_layout)
firstrun_cb = self.create_checkbox(
ALWAYS_OPEN_FIRST_RUN % _("Run Settings dialog"),
@@ -536,7 +525,7 @@ def setup_page(self):
vlayout.addSpacing(10)
vlayout.addWidget(interpreter_group)
vlayout.addWidget(general_group)
- vlayout.addWidget(dedicated_group)
+ vlayout.addWidget(external_group)
vlayout.addWidget(firstrun_cb)
vlayout.addStretch(1)
self.setLayout(vlayout)
diff --git a/spyder/utils/ipython/spyder_kernel.py b/spyder/utils/ipython/spyder_kernel.py
index cf6214f321b..2c8ad8fc996 100644
--- a/spyder/utils/ipython/spyder_kernel.py
+++ b/spyder/utils/ipython/spyder_kernel.py
@@ -252,6 +252,15 @@ def get_env(self):
"""Get environment variables."""
return os.environ.copy()
+ def close_all_mpl_figures(self):
+ """Close all Matplotlib figures."""
+ try:
+ import matplotlib.pyplot as plt
+ plt.close('all')
+ del plt
+ except:
+ pass
+
# -- Private API ---------------------------------------------------
# --- For the Variable Explorer
def _get_current_namespace(self, with_magics=False):
diff --git a/spyder/widgets/ipythonconsole/client.py b/spyder/widgets/ipythonconsole/client.py
index 6ed5c7dd205..d767f36559c 100644
--- a/spyder/widgets/ipythonconsole/client.py
+++ b/spyder/widgets/ipythonconsole/client.py
@@ -432,7 +432,7 @@ def kernel_restarted_message(self, msg):
stderr = self._read_stderr()
except:
stderr = None
- except FileNotFoundError:
+ except OSError:
stderr = None
if stderr:
diff --git a/spyder/widgets/tabs.py b/spyder/widgets/tabs.py
index ac08c2e53ec..8f5e2c28f22 100644
--- a/spyder/widgets/tabs.py
+++ b/spyder/widgets/tabs.py
@@ -35,13 +35,16 @@
class EditTabNamePopup(QLineEdit):
"""Popup on top of the tab to edit its name."""
- def __init__(self, parent):
+ def __init__(self, parent, split_char, split_index):
"""Popup on top of the tab to edit its name."""
# Variables
# Parent (main)
self.main = parent if parent is not None else self.parent()
- # Track with tab is being edited
+ self.split_char = split_char
+ self.split_index = split_index
+
+ # Track which tab is being edited
self.tab_index = None
# Widget setup
@@ -104,7 +107,12 @@ def edit_tab(self, index):
self.move(self.main.mapToGlobal(rect.topLeft()))
# Copies tab name and selects all
- self.setText(self.main.tabText(index))
+ text = self.main.tabText(index)
+ text = text.replace(u'&', u'')
+ if self.split_char:
+ text = text.split(self.split_char)[self.split_index]
+
+ self.setText(text)
self.selectAll()
if not self.isVisible():
@@ -128,7 +136,8 @@ class TabBar(QTabBar):
sig_move_tab = Signal((int, int), (str, int, int))
sig_change_name = Signal(str)
- def __init__(self, parent, ancestor, rename_tabs=False):
+ def __init__(self, parent, ancestor, rename_tabs=False, split_char='',
+ split_index=0):
QTabBar.__init__(self, parent)
self.ancestor = ancestor
@@ -145,7 +154,8 @@ def __init__(self, parent, ancestor, rename_tabs=False):
self.rename_tabs = rename_tabs
if self.rename_tabs:
# Creates tab name editor
- self.tab_name_editor = EditTabNamePopup(self)
+ self.tab_name_editor = EditTabNamePopup(self, split_char,
+ split_index)
else:
self.tab_name_editor = None
@@ -399,10 +409,14 @@ class Tabs(BaseTabs):
def __init__(self, parent, actions=None, menu=None,
corner_widgets=None, menu_use_tooltips=False,
- rename_tabs=False):
+ rename_tabs=False, split_char='',
+ split_index=0):
BaseTabs.__init__(self, parent, actions, menu,
corner_widgets, menu_use_tooltips)
- tab_bar = TabBar(self, parent, rename_tabs=rename_tabs)
+ tab_bar = TabBar(self, parent,
+ rename_tabs=rename_tabs,
+ split_char=split_char,
+ split_index=split_index)
tab_bar.sig_move_tab.connect(self.move_tab)
tab_bar.sig_move_tab[(str, int, int)].connect(
self.move_tab_from_another_tabwidget)