diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index 1c30d3d0007..dc52e00db5c 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -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 @@ -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) @@ -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: diff --git a/spyder/plugins/ipythonconsole/utils/stdfile.py b/spyder/plugins/ipythonconsole/utils/stdfile.py index c0cb22fa454..fb985d2bd3a 100644 --- a/spyder/plugins/ipythonconsole/utils/stdfile.py +++ b/spyder/plugins/ipythonconsole/utils/stdfile.py @@ -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 diff --git a/spyder/plugins/ipythonconsole/widgets/client.py b/spyder/plugins/ipythonconsole/widgets/client.py index f7b32a2cfca..12605ec62cf 100644 --- a/spyder/plugins/ipythonconsole/widgets/client.py +++ b/spyder/plugins/ipythonconsole/widgets/client.py @@ -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"] @@ -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 --------------------------------------------------- diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index c55cb38a378..a4918963805 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -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, @@ -1392,20 +1386,19 @@ 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:

" @@ -1413,12 +1406,11 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, 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) @@ -1435,12 +1427,22 @@ def create_client_for_kernel(self, connection_file, hostname, sshkey, _("Unable to connect to " "%s") % 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( @@ -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: @@ -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: @@ -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.""" @@ -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 @@ -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 @@ -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 @@ -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 @@ -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): @@ -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.""" diff --git a/spyder/plugins/ipythonconsole/widgets/shell.py b/spyder/plugins/ipythonconsole/widgets/shell.py index 3a14cfd9fe5..07b77b00607 100644 --- a/spyder/plugins/ipythonconsole/widgets/shell.py +++ b/spyder/plugins/ipythonconsole/widgets/shell.py @@ -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))