Skip to content

Commit

Permalink
Merge pull request #21540 from ccordoba12/remove-double-message-on-re…
Browse files Browse the repository at this point in the history
…start

PR: Remove double message on restart after kernel dies (IPython console)
  • Loading branch information
ccordoba12 authored Nov 17, 2023
2 parents c749154 + 00bcac3 commit ffecbe2
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 38 deletions.
38 changes: 32 additions & 6 deletions spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# Standard library imports
import os
import os.path as osp
import re
import shutil
import sys
from textwrap import dedent
Expand Down Expand Up @@ -40,8 +41,6 @@
from spyder.plugins.ipythonconsole.tests.conftest import (
get_conda_test_env, get_console_background_color, get_console_font_color,
NEW_DIR, SHELL_TIMEOUT, TEMP_DIRECTORY)
from spyder.plugins.ipythonconsole.utils.kernel_handler import (
KernelConnectionState)
from spyder.plugins.ipythonconsole.widgets import ShellWidget
from spyder.utils.conda import get_list_conda_envs

Expand Down Expand Up @@ -1355,30 +1354,57 @@ def test_conda_env_activation(ipyconsole, qtbot):


@flaky(max_runs=3)
@pytest.mark.parametrize("external_interpreter", [True, False])
@pytest.mark.skipif(os.name == 'nt', reason="no SIGTERM on Windows")
def test_kernel_kill(ipyconsole, qtbot):
def test_kernel_kill(ipyconsole, qtbot, external_interpreter):
"""
Test that the kernel correctly restarts after a kill.
"""
if external_interpreter:
if running_in_ci():
ipyconsole.set_conf('default', False, section='main_interpreter')
pyexec = get_conda_test_env()[1]
ipyconsole.set_conf(
'executable', pyexec, section='main_interpreter'
)
ipyconsole.create_new_client()
else:
# We can't check this locally
return

shell = ipyconsole.get_current_shellwidget()

# Wait for the restarter to start
qtbot.wait(3000)
crash_string = 'import os, signal; os.kill(os.getpid(), signal.SIGTERM)'

# Check only one comm is open
old_open_comms = list(shell.kernel_handler.kernel_comm._comms.keys())
assert len(old_open_comms) == 1
with qtbot.waitSignal(shell.sig_prompt_ready, timeout=30000):
shell.execute(crash_string)
assert crash_string in shell._control.toPlainText()
assert "Restarting kernel..." in shell._control.toPlainText()

console_text = shell._control.toPlainText()
assert crash_string in console_text
assert "The kernel died, restarting..." in console_text

# Check we don't show error generated by `conda run`
assert "conda.cli.main_run" not in console_text

# Check IPython version is shown as expected
assert list(re.finditer(r"IPython \d+\.", console_text))

# Check a new comm replaced the old one
new_open_comms = list(shell.kernel_handler.kernel_comm._comms.keys())
assert len(new_open_comms) == 1
assert old_open_comms[0] != new_open_comms[0]

# Wait until the comm replies
qtbot.waitUntil(
lambda: shell.kernel_handler.kernel_comm._comms[new_open_comms[0]][
'status'] == 'ready')
'status'] == 'ready'
)

assert shell.kernel_handler.kernel_comm._comms[new_open_comms[0]][
'status'] == 'ready'

Expand Down
3 changes: 1 addition & 2 deletions spyder/plugins/ipythonconsole/utils/kernelspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
from spyder.plugins.ipythonconsole import (
SPYDER_KERNELS_CONDA, SPYDER_KERNELS_PIP, SPYDER_KERNELS_VERSION,
SpyderKernelError)
from spyder.utils.conda import (add_quotes, get_conda_env_path, is_conda_env,
find_conda)
from spyder.utils.conda import get_conda_env_path, is_conda_env, find_conda
from spyder.utils.environ import clean_env, get_user_environment_variables
from spyder.utils.misc import get_python_executable
from spyder.utils.programs import (
Expand Down
20 changes: 6 additions & 14 deletions spyder/plugins/ipythonconsole/widgets/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,6 @@ def connect_shellwidget_signals(self):
self.shellwidget.executed.connect(
self.sig_execution_state_changed)

# To show kernel restarted/died messages
self.shellwidget.sig_kernel_restarted_message.connect(
self.kernel_restarted_message)

# To correctly change Matplotlib backend interactively
self.shellwidget.executing.connect(
self.shellwidget.change_mpl_backend)
Expand Down Expand Up @@ -509,7 +505,10 @@ def is_benign_error(self, error):
"Note: Debugging will proceed. "
"Set PYDEVD_DISABLE_FILE_VALIDATION=1 to disable this validation.",
# Argument not expected error. See spyder-ide/spyder#19298
"The following argument was not expected"
"The following argument was not expected",
# Avoid showing error for kernel restarts after kernel dies when
# using an external interpreter
"conda.cli.main_run"
]

return any([err in error for err in benign_errors])
Expand Down Expand Up @@ -614,18 +613,11 @@ def replace_kernel(self, kernel_handler, shutdown_kernel):

# Reset shellwidget and print restart message
self.shellwidget.reset(clear=True)
self.shellwidget.print_restart_message()
self.shellwidget._kernel_restarted_message(died=False)

def print_fault(self, fault):
"""Print fault text."""
self.shellwidget._append_plain_text(
'\n' + fault, before_prompt=True)

@Slot(str)
def kernel_restarted_message(self, msg):
"""Show kernel restarted/died messages."""
self.shellwidget._append_html("<br>%s<hr><br>" % msg,
before_prompt=False)
self.shellwidget._append_plain_text('\n' + fault, before_prompt=True)

@Slot()
def enter_array_inline(self):
Expand Down
10 changes: 7 additions & 3 deletions spyder/plugins/ipythonconsole/widgets/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import os
import os.path as osp
import shlex
import subprocess
import sys

# Third-party imports
Expand Down Expand Up @@ -1368,24 +1369,27 @@ def interpreter_versions(self, path_to_custom_interpreter=None):
ipython_version=release.version
)
else:
import subprocess
versions = {}
pyexec = self.get_conf('executable', section='main_interpreter')
if path_to_custom_interpreter:
pyexec = path_to_custom_interpreter
py_cmd = u'%s -c "import sys; print(sys.version)"' % pyexec

py_cmd = '%s -c "import sys; print(sys.version)"' % pyexec
ipy_cmd = (
u'%s -c "import IPython.core.release as r; print(r.version)"'
'%s -c "import IPython.core.release as r; print(r.version)"'
% pyexec
)

for cmd in [py_cmd, ipy_cmd]:
try:
# Use clean environment
proc = programs.run_shell_command(cmd, env={})
output, _err = proc.communicate()
except subprocess.CalledProcessError:
output = ''

output = output.decode().split('\n')[0].strip()

if 'IPython' in cmd:
versions['ipython_version'] = output
else:
Expand Down
27 changes: 14 additions & 13 deletions spyder/plugins/ipythonconsole/widgets/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ class ShellWidget(NamepaceBrowserWidget, HelpWidget, DebuggingWidget,
# For ShellWidget
sig_focus_changed = Signal()
sig_new_client = Signal()
sig_kernel_restarted_message = Signal(str)

# Kernel died and restarted (not user requested)
sig_prompt_ready = Signal()
Expand Down Expand Up @@ -308,12 +307,6 @@ def reset_kernel_state(self):
self._pdb_recursion_level = 0
self._reading = False

def print_restart_message(self):
"""Print restart message."""
self._append_html(
_("<br>Restarting kernel...<br>"), before_prompt=True)
self.insert_horizontal_ruler()

def call_kernel(self, interrupt=False, blocking=False, callback=None,
timeout=None, display_error=False):
"""
Expand Down Expand Up @@ -731,8 +724,15 @@ def long_banner(self):
'Python %s\n' % py_ver,
'Type "copyright", "credits" or "license" for more information.',
'\n\n',
'IPython %s -- An enhanced Interactive Python.\n' % ipy_ver
]

if ipy_ver:
banner_parts.append(
'IPython %s -- An enhanced Interactive Python.\n' % ipy_ver
)
else:
banner_parts.append('IPython -- An enhanced Interactive Python.\n')

banner = ''.join(banner_parts)

# Pylab additions
Expand Down Expand Up @@ -1215,7 +1215,10 @@ def _banner_default(self):
return self.short_banner()

def _kernel_restarted_message(self, died=True):
msg = _("Kernel died, restarting") if died else _("Kernel restarting")
msg = (
_("The kernel died, restarting...") if died
else _("Restarting kernel...")
)

if died and self.kernel_manager is None:
# The kernel might never restart, show position of fault file
Expand All @@ -1224,15 +1227,13 @@ def _kernel_restarted_message(self, died=True):
+ self.kernel_handler.fault_filename()
)

self.sig_kernel_restarted_message.emit(msg)
self._append_html(f"<br>{msg}<br>", before_prompt=False)
self.insert_horizontal_ruler()

def _handle_kernel_restarted(self, *args, **kwargs):
"""The kernel restarted."""
super()._handle_kernel_restarted(*args, **kwargs)

# Print restart message
self.print_restart_message()

# Reset Pdb state
self.reset_kernel_state()

Expand Down

0 comments on commit ffecbe2

Please sign in to comment.