Skip to content

Commit

Permalink
known_spyder_kernel and std_dir
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentin Peter committed Aug 17, 2022
1 parent db8d0e5 commit 9b415f4
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 90 deletions.
5 changes: 3 additions & 2 deletions spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
from spyder.plugins.help.tests.test_plugin import check_text
from spyder.plugins.help.utils.sphinxify import CSS_PATH
from spyder.plugins.ipythonconsole.plugin import IPythonConsole
from spyder.plugins.ipythonconsole.utils import stdfile
from spyder.plugins.ipythonconsole.utils.style import create_style_class
from spyder.plugins.ipythonconsole.widgets import ClientWidget
from spyder.utils.programs import get_temp_dir
Expand Down Expand Up @@ -209,7 +210,7 @@ def __getattr__(self, attr):

# Create the console and a new client and set environment
os.environ['IPYCONSOLE_TESTING'] = 'True'
os.environ['IPYCONSOLE_TEST_DIR'] = test_dir
stdfile.IPYCONSOLE_TEST_DIR = test_dir
os.environ['IPYCONSOLE_TEST_NO_STDERR'] = test_no_stderr
window = MainWindowMock()
console = IPythonConsole(parent=window, configuration=configuration)
Expand Down Expand Up @@ -274,7 +275,7 @@ def __getattr__(self, attr):
# Close
console.on_close()
os.environ.pop('IPYCONSOLE_TESTING')
os.environ.pop('IPYCONSOLE_TEST_DIR')
stdfile.IPYCONSOLE_TEST_DIR = None
os.environ.pop('IPYCONSOLE_TEST_NO_STDERR')

if os.name == 'nt' or known_leak:
Expand Down
23 changes: 12 additions & 11 deletions spyder/plugins/ipythonconsole/utils/stdfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,27 @@
from spyder.utils.programs import get_temp_dir


def std_filename(connection_file, extension, std_dir=None):
IPYCONSOLE_TEST_DIR = None


def std_filename(connection_file, extension):
"""Filename to save kernel output."""
json_file = osp.basename(connection_file)
file = json_file.split('.json')[0] + extension
if std_dir is not None:
file = osp.join(std_dir, file)
else:
try:
file = osp.join(get_temp_dir(), file)
except (IOError, OSError):
file = None
return file
if IPYCONSOLE_TEST_DIR is not None:
return osp.join(IPYCONSOLE_TEST_DIR, file)
try:
return osp.join(get_temp_dir(), file)
except (IOError, OSError):
return None


class StdFile:
def __init__(self, connection_file, extension=None, std_dir=None):
def __init__(self, connection_file, extension=None):
if extension is None:
self.filename = connection_file
else:
self.filename = std_filename(connection_file, extension, std_dir)
self.filename = std_filename(connection_file, extension)
self._mtime = 0
self._cursor = 0
self._handle = None
Expand Down
4 changes: 2 additions & 2 deletions spyder/plugins/ipythonconsole/widgets/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def start_std_poll(self):
self.std_poll_timer.start()
self.shellwidget.executed.connect(self.poll_std_file_change)

def connect_kernel(self, kernel, known_spyder_kernel=True):
def connect_kernel(self, kernel):
"""Connect kernel to client."""
self.connection_file = kernel["connection_file"]
self.stderr_obj = kernel["stderr_obj"]
Expand All @@ -197,7 +197,7 @@ def connect_kernel(self, kernel, known_spyder_kernel=True):
self.shellwidget.connect_kernel(
kernel["kernel_client"],
kernel["kernel_manager"],
known_spyder_kernel
kernel["known_spyder_kernel"]
)

# ----- Private methods ---------------------------------------------------
Expand Down
138 changes: 64 additions & 74 deletions spyder/plugins/ipythonconsole/widgets/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,8 @@ def __init__(self, name=None, plugin=None, parent=None):

# Attrs for testing
self._testing = bool(os.environ.get('IPYCONSOLE_TESTING'))
self._test_dir = os.environ.get('IPYCONSOLE_TEST_DIR')
self._test_no_stderr = os.environ.get('IPYCONSOLE_TEST_NO_STDERR')

# Create temp dir on testing to save kernel errors
if self._test_dir:
if not osp.isdir(osp.join(self._test_dir)):
os.makedirs(osp.join(self._test_dir))

layout = QVBoxLayout()
layout.setSpacing(0)
self.tabwidget = Tabs(self, menu=self._options_menu,
Expand Down Expand Up @@ -1392,33 +1386,31 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False,
give_focus=give_focus,
handlers=self.registered_spyder_kernel_handlers
)

# Add client to widget
self.add_tab(
client, name=client.get_name(), filename=filename,
give_focus=give_focus)

std_dir = self._test_dir if self._test_dir else None
# Create new kernel
kernel_spec = self.create_kernel_spec(
is_cython=is_cython,
is_pylab=is_pylab,
is_sympy=is_sympy
)
try:
kernel = self.get_new_kernel(
kernel_spec, std_dir=std_dir, cache=cache)
kernel = self.get_cached_kernel(kernel_spec, cache=cache)
except Exception:
# See spyder-ide/spyder#7302.
error_msg = _("The error is:<br><br>"
"<tt>{}</tt>").format(traceback.format_exc())
client.show_kernel_error(error_msg)
return

# Connect kernel to client
client.connect_kernel(kernel)

def create_client_for_kernel(self, connection_file, hostname, sshkey,
password):
"""Create a client connected to an existing kernel."""
# Verifying if the connection file exists
def check_connection_file(self, connection_file):
"""Verify if the connection file exists"""
try:
cf_path = osp.dirname(connection_file)
cf_filename = osp.basename(connection_file)
Expand All @@ -1435,12 +1427,22 @@ def create_client_for_kernel(self, connection_file, hostname, sshkey,
_("Unable to connect to "
"<b>%s</b>") % connection_file)
return
return connection_file


def create_client_for_kernel(self, connection_file, hostname, sshkey,
password):
"""Create a client connected to an existing kernel."""

connection_file = self.check_connection_file(connection_file)
if connection_file is None:
return

# Getting the master id that corresponds to the client
# (i.e. the i in i/A)
master_id = None
given_name = None
known_spyder_kernel = False

slave_ord = ord('A') - 1
t0 = None
kernel = dict(
Expand All @@ -1449,7 +1451,8 @@ def create_client_for_kernel(self, connection_file, hostname, sshkey,
connection_file=None,
stderr_obj=None,
stdout_obj=None,
fault_obj=None)
fault_obj=None,
known_spyder_kernel=False)

for cl in self.clients:
if connection_file in cl.connection_file:
Expand All @@ -1459,7 +1462,8 @@ def create_client_for_kernel(self, connection_file, hostname, sshkey,
t0 = cl.t0
if master_id is None:
master_id = cl.id_['int_id']
known_spyder_kernel = cl.shellwidget.is_spyder_kernel
kernel["known_spyder_kernel"] = (
cl.shellwidget.is_spyder_kernel)
if cl.stderr_obj:
kernel["stderr_obj"] = cl.stderr_obj.copy()
if cl.stdout_obj:
Expand Down Expand Up @@ -1511,7 +1515,7 @@ def create_client_for_kernel(self, connection_file, hostname, sshkey,
kernel["connection_file"] = connection_file
kernel["kernel_client"] = kernel_client

client.connect_kernel(kernel, known_spyder_kernel)
client.connect_kernel(kernel)

def new_kernel_client(self, connection_file, hostname, sshkey, password):
"""Create kernel client."""
Expand Down Expand Up @@ -1562,21 +1566,20 @@ def create_cython_client(self):
"""Force creation of Cython client"""
self.create_new_client(is_cython=True, given_name="Cython")

def check_cached_kernel_spec(self, kernel_spec, std_dir):
def check_cached_kernel_spec(self, kernel_spec):
"""Test if kernel_spec corresponds to the cached kernel_spec."""
if self._cached_kernel_properties is None:
return False
(cached_spec, cached_env, cached_argv, cached_dir, cached_kernel
(cached_spec, cached_env, cached_argv, cached_kernel
) = self._cached_kernel_properties
# Call interrupt_mode so the dict will be the same
kernel_spec.interrupt_mode
cached_spec.interrupt_mode
return (std_dir == cached_dir
and cached_spec.__dict__ == kernel_spec.__dict__
return (cached_spec.__dict__ == kernel_spec.__dict__
and kernel_spec.argv == cached_argv
and kernel_spec.env == cached_env)

def get_new_kernel(self, kernel_spec, std_dir=None, cache=True):
def get_cached_kernel(self, kernel_spec, cache=True):
"""Get a new kernel, and cache one for next time."""
# Check if ipykernel is present in the external interpreter.
# Else we won't be able to create a client
Expand Down Expand Up @@ -1614,7 +1617,7 @@ def get_new_kernel(self, kernel_spec, std_dir=None, cache=True):
)

# Cache another kernel for next time.
new_kernel = self.create_new_kernel(kernel_spec, std_dir)
new_kernel = self.create_new_kernel(kernel_spec)

if not cache:
# remove/don't use cache if requested
Expand All @@ -1625,7 +1628,7 @@ def get_new_kernel(self, kernel_spec, std_dir=None, cache=True):
cached_kernel = None
if self._cached_kernel_properties is not None:
cached_kernel = self._cached_kernel_properties[-1]
if not self.check_cached_kernel_spec(kernel_spec, std_dir):
if not self.check_cached_kernel_spec(kernel_spec):
# Close the kernel
self.close_cached_kernel()
cached_kernel = None
Expand All @@ -1635,11 +1638,10 @@ def get_new_kernel(self, kernel_spec, std_dir=None, cache=True):
kernel_spec,
kernel_spec.env,
kernel_spec.argv,
std_dir,
new_kernel)

if cached_kernel is None:
return self.create_new_kernel(kernel_spec, std_dir)
return self.create_new_kernel(kernel_spec)

return cached_kernel

Expand All @@ -1662,37 +1664,54 @@ def close_cached_kernel(self):
if fault_obj:
fault_obj.remove()

def create_new_kernel(self, kernel_spec, std_dir=None):
"""Create a new kernel."""
def create_new_kernel(self, kernel_spec):
"""
Create a new kernel.
Might raise all kinds of exceptions
"""
connection_file = self._new_connection_file()
if connection_file is None:
raise RuntimeError(
self.PERMISSION_ERROR_MSG.format(jupyter_runtime_dir()))

stderr_obj = None
stderr_handle = None
stdout_obj = None
stdout_handle = None
if not self._test_no_stderr:
stderr_obj = StdFile(connection_file, '.stderr', std_dir)
stderr_handle = stderr_obj.handle
stdout_obj = StdFile(connection_file, '.stdout', std_dir)
stdout_handle = stdout_obj.handle
fault_obj = StdFile(connection_file, '.fault', std_dir)

km, kc = self.create_kernel_manager_and_kernel_client(
connection_file,
stderr_handle,
stdout_handle,
kernel_spec,
stderr_obj = StdFile(connection_file, '.stderr')
stdout_obj = StdFile(connection_file, '.stdout')
fault_obj = StdFile(connection_file, '.fault')

# Kernel manager
kernel_manager = SpyderKernelManager(
connection_file=connection_file,
config=None,
autorestart=True,
)

kernel_manager._kernel_spec = kernel_spec

kernel_manager.start_kernel(
stderr=stderr_obj.handle,
stdout=stdout_obj.handle,
env=kernel_spec.env
)

# Kernel client
kernel_client = kernel_manager.client()

# Increase time (in seconds) to detect if a kernel is alive.
# See spyder-ide/spyder#3444.
kernel_client.hb_channel.time_to_dead = 25.0

return dict(
connection_file=connection_file,
kernel_manager=km,
kernel_client=kc,
kernel_manager=kernel_manager,
kernel_client=kernel_client,
stderr_obj=stderr_obj,
stdout_obj=stdout_obj,
fault_obj=fault_obj)
fault_obj=fault_obj,
known_spyder_kernel=True)

@Slot(str)
def create_client_from_path(self, path):
Expand Down Expand Up @@ -2004,35 +2023,6 @@ def create_kernel_spec(self, is_cython=False,
is_pylab=is_pylab,
is_sympy=is_sympy)

def create_kernel_manager_and_kernel_client(self, connection_file,
stderr_handle,
stdout_handle,
kernel_spec):
"""Create kernel manager and client."""
# Kernel manager
kernel_manager = SpyderKernelManager(
connection_file=connection_file,
config=None,
autorestart=True,
)

kernel_manager._kernel_spec = kernel_spec

kernel_manager.start_kernel(
stderr=stderr_handle,
stdout=stdout_handle,
env=kernel_spec.env
)

# Kernel client
kernel_client = kernel_manager.client()

# Increase time (in seconds) to detect if a kernel is alive.
# See spyder-ide/spyder#3444.
kernel_client.hb_channel.time_to_dead = 25.0

return kernel_manager, kernel_client

@Slot()
def restart_kernel(self, client=None, ask_before_restart=True):
"""Restart kernel of current client."""
Expand Down
2 changes: 1 addition & 1 deletion spyder/plugins/ipythonconsole/widgets/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def call_kernel(self, interrupt=False, blocking=False, callback=None,
)

def connect_kernel(
self, kernel_client, kernel_manager, known_spyder_kernel=True):
self, kernel_client, kernel_manager, known_spyder_kernel):
"""Connect to kernel."""
kernel_client.stopped_channels.connect(
lambda: self.sig_shellwidget_deleted.emit(self))
Expand Down

0 comments on commit 9b415f4

Please sign in to comment.