Repository: spyder-ide/spyder

Issue:
Spyder 3.1.2 cannot connect to kernel as 3.0.2
I can choose the file directory to connect to a kernel like "kernel-100.json",
but when I just type 100, It shows that cannot connect to kernel, however this can be done in spyder 3.0.2


Discussion:
@mariacamilaremolinagutierrez, this is a regression over our previous behavior. Please fix it.

Resolving patch:
diff --git a/spyder/plugins/ipythonconsole.py b/spyder/plugins/ipythonconsole.py
--- a/spyder/plugins/ipythonconsole.py
+++ b/spyder/plugins/ipythonconsole.py
@@ -206,12 +206,12 @@ def __init__(self, parent=None):
         ssh_form.addRow(_('Password'), self.pw)
         
         # Ok and Cancel buttons
-        accept_btns = QDialogButtonBox(
+        self.accept_btns = QDialogButtonBox(
             QDialogButtonBox.Ok | QDialogButtonBox.Cancel,
             Qt.Horizontal, self)
 
-        accept_btns.accepted.connect(self.accept)
-        accept_btns.rejected.connect(self.reject)
+        self.accept_btns.accepted.connect(self.accept)
+        self.accept_btns.rejected.connect(self.reject)
 
         # Dialog layout
         layout = QVBoxLayout(self)
@@ -219,7 +219,7 @@ def __init__(self, parent=None):
         layout.addLayout(cf_layout)
         layout.addWidget(self.rm_cb)
         layout.addLayout(ssh_form)
-        layout.addWidget(accept_btns)
+        layout.addWidget(self.accept_btns)
                 
         # remote kernel checkbox enables the ssh_connection_form
         def ssh_set_enabled(state):
@@ -242,8 +242,9 @@ def select_ssh_key(self):
         self.kf.setText(kf)
 
     @staticmethod
-    def get_connection_parameters(parent=None):
-        dialog = KernelConnectionDialog(parent)
+    def get_connection_parameters(parent=None, dialog=None):
+        if not dialog:
+            dialog = KernelConnectionDialog(parent)
         result = dialog.exec_()
         is_remote = bool(dialog.rm_cb.checkState())
         accepted = result == QDialog.Accepted
@@ -255,7 +256,11 @@ def get_connection_parameters(parent=None):
                 falsy_to_none(dialog.pw.text()), # ssh password
                 accepted)                        # ok
         else:
-            return (dialog.cf.text(), None, None, None, accepted)
+            path = dialog.cf.text()
+            _dir, filename = osp.dirname(path), osp.basename(path)
+            if _dir == '' and not filename.endswith('.json'):
+                path = osp.join(jupyter_runtime_dir(), 'kernel-'+path+'.json')
+            return (path, None, None, None, accepted)
 
 
 #------------------------------------------------------------------------------


Test patch:
diff --git a/spyder/plugins/tests/test_ipythonconsole.py b/spyder/plugins/tests/test_ipythonconsole.py
--- a/spyder/plugins/tests/test_ipythonconsole.py
+++ b/spyder/plugins/tests/test_ipythonconsole.py
@@ -5,31 +5,126 @@
 #
 
 import os
+import os.path as osp
+import shutil
+import tempfile
 
 import pytest
-from qtpy.QtCore import Qt
-from pytestqt import qtbot
-from spyder.py3compat import to_text_string
-from spyder.plugins.ipythonconsole import IPythonConsole
+from qtpy.QtCore import Qt, QTimer
+from qtpy.QtWidgets import QApplication
 
+from spyder.plugins.ipythonconsole import (IPythonConsole,
+                                           KernelConnectionDialog)
 
+
+#==============================================================================
+# Constants
+#==============================================================================
+SHELL_TIMEOUT = 30000
+
+
+#==============================================================================
+# Utillity Functions
+#==============================================================================
+def open_client_from_connection_file(connection_info, qtbot):
+    top_level_widgets = QApplication.topLevelWidgets()
+    for w in top_level_widgets:
+        if isinstance(w, KernelConnectionDialog):
+            w.cf.setText(connection_info)
+            qtbot.keyClick(w, Qt.Key_Enter)
+
+
+#==============================================================================
 # Qt Test Fixtures
-#--------------------------------
+#==============================================================================
 @pytest.fixture
 def ipyconsole():
     widget = IPythonConsole(None, testing=True)
     widget.create_new_client()
+    widget.show()
     return widget
 
 
+#==============================================================================
 # Tests
-#-------------------------------
-@pytest.mark.skipif(os.name == 'nt', reason="It's timing out on Windows")
-def test_sys_argv_clear(ipyconsole, qtbot):
+#==============================================================================
+def test_load_kernel_file_from_id(ipyconsole, qtbot):
+    """
+    Test that a new client is created using its id
+    """
+    shell = ipyconsole.get_current_shellwidget()
+    client = ipyconsole.get_current_client()
+    qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
+
+    connection_file = osp.basename(client.connection_file)
+    id_ = connection_file.split('kernel-')[-1].split('.json')[0]
+
+    QTimer.singleShot(2000, lambda: open_client_from_connection_file(
+                                        id_, qtbot))
+    ipyconsole.create_client_for_kernel()
+
+    new_client = ipyconsole.get_clients()[1]
+    assert new_client.name == '1/B'
+
+    ipyconsole.close()
+
+
+def test_load_kernel_file_from_location(ipyconsole, qtbot):
+    """
+    Test that a new client is created using a connection file
+    placed in a different location from jupyter_runtime_dir
+    """
     shell = ipyconsole.get_current_shellwidget()
     client = ipyconsole.get_current_client()
+    qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
+
+    connection_file = osp.join(tempfile.gettempdir(),
+                               osp.basename(client.connection_file))
+    shutil.copy2(client.connection_file, connection_file)
+
+    QTimer.singleShot(2000, lambda: open_client_from_connection_file(
+                                        connection_file,
+                                        qtbot))
+    ipyconsole.create_client_for_kernel()
+
+    assert len(ipyconsole.get_clients()) == 2
+
+    ipyconsole.close()
+
+
+def test_load_kernel_file(ipyconsole, qtbot):
+    """
+    Test that a new client is created using the connection file
+    of an existing client
+    """
+    shell = ipyconsole.get_current_shellwidget()
+    client = ipyconsole.get_current_client()
+    qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
+
+    QTimer.singleShot(2000, lambda: open_client_from_connection_file(
+                                        client.connection_file,
+                                        qtbot))
+    ipyconsole.create_client_for_kernel()
+
+    new_client = ipyconsole.get_clients()[1]
+    new_shell = new_client.shellwidget
+    qtbot.waitUntil(lambda: new_shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
+    new_shell.execute('a = 10')
+
+    assert new_client.name == '1/B'
+    assert shell.get_value('a') == new_shell.get_value('a')
+
+    ipyconsole.close()
+
+
+@pytest.mark.skipif(os.name == 'nt', reason="It times out on Windows")
+def test_sys_argv_clear(ipyconsole, qtbot):
+    """Test that sys.argv is cleared up correctly"""
+    shell = ipyconsole.get_current_shellwidget()
+    qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
 
-    qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=6000)
     shell.execute('import sys; A = sys.argv')
     argv = shell.get_value("A")
     assert argv == ['']
+
+    ipyconsole.close()