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: Wait until console is ready before executing code for dedicated consoles #5301

Merged
merged 9 commits into from
Oct 12, 2017
9 changes: 9 additions & 0 deletions spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ def test_dedicated_consoles(main_window, qtbot):
qtbot.keyClick(code_editor, Qt.Key_F5)
qtbot.wait(500)
shell = main_window.ipyconsole.get_current_shellwidget()
control = shell._control
qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
nsb = main_window.variableexplorer.get_focus_widget()

Expand All @@ -304,13 +305,21 @@ def test_dedicated_consoles(main_window, qtbot):
qtbot.wait(500)
assert nsb.editor.model.rowCount() == 3

# --- Assert only runfile text is present and there's no banner text ---
# See PR #5301
text = control.toPlainText()
assert ('runfile' in text) and not ('Python' in text or 'IPython' in text)

# --- Clean namespace after re-execution ---
with qtbot.waitSignal(shell.executed):
shell.execute('zz = -1')
qtbot.keyClick(code_editor, Qt.Key_F5)
qtbot.wait(500)
assert not shell.is_defined('zz')

# --- Assert runfile text is present after reruns ---
assert 'runfile' in control.toPlainText()

# ---- Closing test file and resetting config ----
main_window.editor.close_file()
CONF.set('run', 'configurations', [])
Expand Down
19 changes: 15 additions & 4 deletions spyder/plugins/ipythonconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
from spyder.utils.ipython.style import create_qss_style
from spyder.utils.qthelpers import create_action, MENU_SEPARATOR
from spyder.utils import icon_manager as ima
from spyder.utils import encoding, programs, sourcecode
from spyder.utils import programs, sourcecode
from spyder.utils.programs import TEMPDIR
from spyder.utils.misc import get_error_match, remove_backslashes
from spyder.widgets.findreplace import FindReplace
Expand Down Expand Up @@ -860,13 +860,15 @@ def run_script(self, filename, wdir, args, debug, post_mortem,
norm = lambda text: remove_backslashes(to_text_string(text))

# Select client to execute code on it
is_new_client = False
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()
is_new_client = True

if client is not None:
# Internal kernels, use runfile
Expand All @@ -888,7 +890,16 @@ def run_script(self, filename, wdir, args, debug, post_mortem,
if args:
line += " %s" % norm(args)
try:
self.execute_code(line, current_client, clear_variables)
if current_client:
self.execute_code(line, current_client, clear_variables)
else:
if is_new_client:
client.shellwidget.silent_execute('%clear')
else:
client.shellwidget.execute('%clear')
client.shellwidget.sig_prompt_ready.connect(
lambda: self.execute_code(line, current_client,
clear_variables))
except AttributeError:
pass
self.visibility_changed(True)
Expand Down Expand Up @@ -930,7 +941,7 @@ def execute_code(self, lines, current_client=True, clear_variables=False):
if not current_client:
# Clear console and reset namespace for
# dedicated clients
sw.silent_execute('%clear')
sw.sig_prompt_ready.disconnect()
sw.silent_execute(
'get_ipython().kernel.close_all_mpl_figures()')
sw.reset_namespace(warning=False, silent=True)
Expand Down Expand Up @@ -1427,7 +1438,7 @@ def create_kernel_manager_and_kernel_client(self, connection_file,

# Increase time to detect if a kernel is alive
# See Issue 3444
kernel_client.hb_channel.time_to_dead = 6.0
kernel_client.hb_channel.time_to_dead = 18.0

return kernel_manager, kernel_client

Expand Down
2 changes: 2 additions & 0 deletions spyder/utils/ipython/start_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ def kernel_config():
# Until we implement Issue 1052
spy_cfg.InteractiveShell.xmode = 'Plain'

# Using Jedi slow completions a lot for objects
# with big repr's
spy_cfg.IPCompleter.use_jedi = False

# Run lines of code at startup
Expand Down
14 changes: 5 additions & 9 deletions spyder/widgets/ipythonconsole/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,6 @@ def __init__(self, plugin, id_,
# --- Exit function
self.exit_callback = lambda: plugin.close_client(client=self)

# --- Signals
# As soon as some content is printed in the console, stop
# our loading animation
document = self.get_control().document()
document.contentsChange.connect(self._hide_loading_page)

# --- Dialog manager
self.dialog_manager = DialogManager()

Expand Down Expand Up @@ -215,11 +209,15 @@ def configure_shellwidget(self, give_focus=True):
# To sync with working directory toolbar
self.shellwidget.executed.connect(self.shellwidget.get_cwd)

# To apply style
if not create_qss_style(self.shellwidget.syntax_style)[1]:
self.shellwidget.silent_execute("%colors linux")
else:
self.shellwidget.silent_execute("%colors lightbg")

# To hide the loading page
self.shellwidget.sig_prompt_ready.connect(self._hide_loading_page)

def enable_stop_button(self):
self.stop_button.setEnabled(True)

Expand Down Expand Up @@ -516,9 +514,7 @@ def _hide_loading_page(self):
self.infowidget.hide()
self.shellwidget.show()
self.infowidget.setHtml(BLANK)

document = self.get_control().document()
document.contentsChange.disconnect(self._hide_loading_page)
self.shellwidget.sig_prompt_ready.disconnect(self._hide_loading_page)

def _read_stderr(self):
"""Read the stderr file of the kernel."""
Expand Down
7 changes: 7 additions & 0 deletions spyder/widgets/ipythonconsole/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class ShellWidget(NamepaceBrowserWidget, HelpWidget, DebuggingWidget):
sig_got_reply = Signal()
sig_is_spykernel = Signal(object)
sig_kernel_restarted = Signal(str)
sig_prompt_ready = Signal()

# For global working directory
sig_change_cwd = Signal(str)
Expand Down Expand Up @@ -468,6 +469,12 @@ def _syntax_style_changed(self):
else:
self._highlighter.set_style_sheet(self.style_sheet)

def _prompt_started_hook(self):
"""Emit a signal when the prompt is ready."""
if not self._reading:
self._highlighter.highlighting_on = True
self.sig_prompt_ready.emit()

#---- Qt methods ----------------------------------------------------------
def focusInEvent(self, event):
"""Reimplement Qt method to send focus change notification"""
Expand Down