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: Add detection of external Spyder kernels #4670

Merged
merged 11 commits into from
Jun 30, 2017

Conversation

dalthviz
Copy link
Member

Fixes #4664

@@ -45,6 +45,7 @@ class ShellWidget(NamepaceBrowserWidget, HelpWidget, DebuggingWidget):
focus_changed = Signal()
new_client = Signal()
sig_got_reply = Signal()
sig_spyder_kernel = Signal()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call this sig_is_spykernel

@@ -77,6 +78,14 @@ def is_running(self):
else:
return False

def is_spyder_kernel(self):
"""."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing docstring

# attach it to other plugins
self.shellwidget.sig_spyder_kernel.connect(
lambda : self.sig_spyder_kernel.emit(self))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need for these changes in client.py. We can directly connect to the signal emitted by shellwidget in ipythonconsole.py.

So please remove the signal above and this code here.

@@ -1390,6 +1390,13 @@ def process_finished(self, client):
if self.variableexplorer is not None:
self.variableexplorer.remove_shellwidget(id(client.shellwidget))

def connect_shellwidget(self, client):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connect_shellwidget -> connect_external_kernel_to_varexp

kc = client.shellwidget.kernel_client
self.process_started(client)
client.shellwidget.set_namespace_view_settings()
kc.stopped_channels.connect(lambda c=client: self.process_finished(c))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rewrite this as

sw = client.shellwidget
kc = sw.kernel_client

self.process_started(client)
sw.set_namespace_view_settings()
kc.stopped_channels.connect(lambda c=client: self.process_finished(c))


# Assign kernel manager and client to shellwidget
client.shellwidget.kernel_client = kernel_client
client.shellwidget.kernel_manager = kernel_manager
client.sig_spyder_kernel.connect(self.connect_shellwidget)
kernel_client.start_channels()
client.shellwidget.is_spyder_kernel()
Copy link
Member

@ccordoba12 ccordoba12 Jun 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These three lines only apply when master_name is None because we don't want to do it for clients connected to kernels already created by Spyder.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of master_name is None should we create a validation with the external_kernel variable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation is precisely done here. We iterate over all open clients in the IPython console to see if we're trying to connect to the kernel of a current client. If that's the case, we don't need to connect that kernel to the Variable Explorer because it's already connected.

If that fails, then it means we're trying to connect to an external kernel, in which case we want to connect it to the Variable Explorer (if it's a Spyder kernel, of course).

What you've done was the right thing to do :-)


# Assign kernel manager and client to shellwidget
client.shellwidget.kernel_client = kernel_client
client.shellwidget.kernel_manager = kernel_manager
client.sig_spyder_kernel.connect(self.connect_shellwidget)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be

client.shellwidget.sig_is_spkernel.connect(
    self.connect_external_kernel_to_varexp)

@@ -1390,6 +1390,18 @@ def process_finished(self, client):
if self.variableexplorer is not None:
self.variableexplorer.remove_shellwidget(id(client.shellwidget))

def connect_external_kernel_to_varexp(self, shellwidget):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot about connecting to our Help, so let's rename this to connect_to_external_kernel

@@ -1390,6 +1390,18 @@ def process_finished(self, client):
if self.variableexplorer is not None:
self.variableexplorer.remove_shellwidget(id(client.shellwidget))

def connect_external_kernel_to_varexp(self, shellwidget):
"""Connect a shellwidget to the variable explorer."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be

Connect an external kernel to the Variable Explorer and Help.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, we need to be more specific

"""
Connect an external kernel to the Variable Explorer and Help, if
it is a Spyder kernel.
"""

@ccordoba12 ccordoba12 changed the title PR: Add detection of Spyder kernels PR: Add detection of external Spyder kernels Jun 29, 2017
@ccordoba12
Copy link
Member

ccordoba12 commented Jun 29, 2017

This needs tests. For that, please copy the function called start_new_kernel, from

https://github.com/jupyter/jupyter_client/blob/master/jupyter_client/manager.py#L445

to app/tests/test_mainwindow.py, and add to it a new parameter called spykernel=False, and inside it this content:

...
km = KernelManager(kernel_name=kernel_name)
if spykernel:
    km._kernel_spec = SpyderKernelSpec()
...

Using this function, you need to create two kernels (inside the same test): a regular kernel and a Spyder kernel. Then you connect a client to each one of them, run something in them (e.g. a = 10) and finally verify that for the regular kernel there's nothing shown in the Variable Explorer and for the Spyder kernel there's one row present.

To get the connection file of those kernels, you need this

km, kc = start_new_kernel()
kc.connection_file

And to verify how many rows there are in the Variable Explorer, please use this (inside the test, of course :-):

main_window.variableexplorer.visibility_changed(True)
nsb = main_window.variableexplorer.get_focus_widget()
assert nsb.editor.model.rowCount() == 1

@ccordoba12
Copy link
Member

And at end of test you need to shutdown those kernels with

km.shutdown_kernel(now=True)

@@ -105,6 +126,42 @@ def close_window():
# Tests
#==============================================================================
@flaky(max_runs=3)
def test_kernel_connection(main_window, qtbot):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_kernel_connection -> test_connection_to_external_kernel

@@ -105,6 +126,42 @@ def close_window():
# Tests
#==============================================================================
@flaky(max_runs=3)
def test_kernel_connection(main_window, qtbot):
"""Test that a kernel from Spyder is detected and attached to variable explorer."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this to

Test that only Spyder kernels are connected to the Variable Explorer

shell = main_window.ipyconsole.get_current_shellwidget()
qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
with qtbot.waitSignal(shell.executed):
shell.execute('a = 0')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a = 10


# Assert that there are no variables in the variable explorer
main_window.variableexplorer.visibility_changed(True)
nsb = main_window.variableexplorer.get_focus_widget()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add qtbot.wait(500) below this line to wait a little bit until the result appears in the Variable Explorer

Copy link
Member

@ccordoba12 ccordoba12 Jun 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember that all these operations are asynchronous

shell = main_window.ipyconsole.get_current_shellwidget()
qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
with qtbot.waitSignal(shell.executed):
shell.execute('a = 0')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a = 10

# Assert that a variable is visible in the variable explorer
main_window.variableexplorer.visibility_changed(True)
nsb = main_window.variableexplorer.get_focus_widget()
qtbot.waitUntil(lambda: nsb.editor.model.rowCount() == 1, timeout=500)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this line simply to

qtbot.wait(500)

main_window.variableexplorer.visibility_changed(True)
nsb = main_window.variableexplorer.get_focus_widget()
qtbot.waitUntil(lambda: nsb.editor.model.rowCount() == 1, timeout=500)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove blank

def _create_client_for_kernel(self, connection_file, hostname, sshkey,
password):
# Verifying if the connection file exists
try:
cf_path = osp.dirname(connection_file)
cf_filename = osp.basename(connection_file)
# To change a possible empty string to None
falsy_to_none = lambda arg: arg if arg else None
cf_path = falsy_to_none(cf_path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need of that falsy_to_none function. You can simply write

cf_path = cf_path if cf_path else None

main_window.ipyconsole._create_client_for_kernel(kc.connection_file, None,
None, None)
shell = main_window.ipyconsole.get_current_shellwidget()
qtbot.waitUntil(lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot to remove them from here :-)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is needed to update the shell variable and get the current shellwidget (to execute the code in the new console with the kernel spyder), no?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right! Then please remove the same lines at the beginning of the test because it makes no sense to have them there.

main_window.variableexplorer.visibility_changed(True)
nsb = main_window.variableexplorer.get_focus_widget()
qtbot.wait(500)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this blank line.

@ccordoba12
Copy link
Member

@dalthviz, you committed some garbage files and commented some tests too in your last commit, so please redo it and make a forced push to not have that commit as part of this PR.

@dalthviz
Copy link
Member Author

@ccordoba12 you are right, sorry for that, now it is ok 👍

@ccordoba12
Copy link
Member

@dalthviz, please move test_calltip to be the first one on test_mainwindow.py because it's timing out in Appveyor if run after test_connection_to_external_kernel.

@ccordoba12
Copy link
Member

@dalthviz, after you solve my last (simple) comments, this is ready to be merged!

Copy link
Member

@ccordoba12 ccordoba12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @dalthviz for your hard work on this one.

@ccordoba12 ccordoba12 merged commit 9a92ca0 into spyder-ide:3.x Jun 30, 2017
ccordoba12 added a commit that referenced this pull request Jun 30, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants