-
Notifications
You must be signed in to change notification settings - Fork 284
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
add BlockingKernelClient.execute_interactive #185
Conversation
cb9602f
to
fd2bc4c
Compare
I didn't really like calling this @takluyver what do you think about BlockingClient adding a |
Captures output, returns reply
fd2bc4c
to
fdb9356
Compare
waits for and returns reply instead of msg_id
05ed320
to
9967225
Compare
I like the |
None is not valid in this case
Thanks, @minrk ... this works pretty well! One thing I am having trouble with is |
@dsblank not the way I've implemented it, I didn't include support for stdin requests, but I can. |
@dsblank it handles input now. |
Parameters | ||
---------- | ||
""") | ||
parts.append(""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit leary of the docstring munging, because I don't think we'll remember it when editing docstrings - e.g. if we add a note below 'returns', it will be chopped off. Not a big deal, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, me, too. I couldn't think of a better way, though. If you have a less gross idea, I'm all for it.
The main reason I did this is that I can't see a good way to get a nice signature for the wrapped functions. If I had a good signature, I'd be happier with a simpler "See KernelClient.method for more details...". It's easy to inherit the wrapped method's signature exactly, but I didn't see a good way to do that and add the reply, timeout arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're willing to depend on (or bundle) a backported version of the Py3 inspect.signature machinery for Py2, it should be relatively easy to add a couple of parameters to a signature and set the f.__signature__
attribute. IIRC, IPython's inspection will use that information even on Python 2, which is good enough for me.
Works great! |
stream.write(content['text']) | ||
elif msg_type in ('display_data', 'execute_result', 'error'): | ||
if in_kernel: | ||
session.send(socket, msg_type, content, parent=parent_header) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit uncomfortable about this, because it's making a general method aware of a certain specific context in which it may be running.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough. What would you prefer? What I really want is for rich Jupyter display methods to be fully redisplayed when run in a an IPython kernel, and for plaintext output to be shown in a simple terminal way when that's the case.
Do you think these should be different methods, or explicit user-provided flags?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would actually make this back to being a separate method. I think the kw arg works for the general reply=True
to wait for the response, but this feels like another level on top of what the kernel client methods usually do. Maybe execute_interactive()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me. I'll leave execute(reply=True)
matching the other methods, without touching output.
@minrk This issue is also a problem for non-ipython kernels. That is, we have to monkeypatch IPython.display.display in order to get rich display. If a kernel had a special method for this, then in_kernel and non-ipython kernels could both call it. |
@dsblank good point. What do you folks think? Special method or argument for kernel-aware behavior? |
Honestly, I do think that the default behavior in IPython should be to redisplay rich output, so detecting IPython kernels by default still seems like the right choice to me. But I take @dsblank's point that to make it more generally useful, there should be some ways to pass explicit arguments in addition to that. |
@minrk That makes sense (there has to be a default). Just remember that some languages will make this the interface to their language. As long as there is some method for dealing with the issue that is not too painful, that should work. Thanks for this! |
output_hook is called with output messages, so that other clients can use this (e.g. nbconvert) Default is to redisplay, including detection of IPython kernel for rich outputs.
2b37871
to
9687697
Compare
It is now |
I guess it should have a stdin_hook as well, then. Pfff, this is growing complexity rapidly. I think I'd be inclined to say it's intended as a terminal method, stick to displaying text output, and maybe offer a hook just for rich output (display_data/execute_result). |
I would disagree with this, since the main reason I want this is to use it for interacting with kernels from notebooks. |
so that input request handling can be customized
Fair enough. I'm just disappointed that something that originally seemed quite simple has now ballooned into a fair amount of extra complexity. |
Fair enough. I don't feel bad, though, as the extra complexity should also mean that it ought to be able to replace a bunch of similar code in existing projects (nbconvert, jupyter_console, dask) once this is out. So to me it's more a consolidation of code to where it belongs. |
allow_stdin = self.allow_stdin | ||
if allow_stdin and not self.stdin_channel.is_alive(): | ||
raise RuntimeError("stdin channel must be running to allow input") | ||
msg_id = super(BlockingKernelClient, self).execute(code, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't need to be a super()
call now that it's a separate method, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, fixed.
(annoying)
Other than that comment, this looks OK. I've milestoned it for 5.0 as it's clearly a new feature, but I won't get upset if we want to call it 4.4. |
Since 4.4 is about ready to ship, I'm happy calling it 4.5. I don't know that we have any breakages planned that would cause 5.0 in the forseeable future, though. |
(If there is one, 5.0 is fine, too) |
Fair enough. I'm never entirely sure whether 4.x+1 releases are feature releases or bugfix releases. |
We are on minor releases until there's a big and/or breaking change that prompt the major version bump. This doesn't happen often on the smaller repos, so I don't expect to have things like backport branches of ipykernel or jupyter_client or nbformat. It happens almost immediately on the big, fast repos like IPython and notebook, but there aren't many of those. |
I made jupyter/nbconvert#360 to see if this works over there. It's almost enough, but there are two features over there that I haven't implemented here:
|
I can save those for another PR, though |
Is there a way that this can be used to echo input and traceback in a remote client? #example.py
from jupyter_client import find_connection_file, BlockingKernelClient
kc = BlockingKernelClient(connection_file = find_connection_file())
kc.load_connection_file()
kc.execute_interactive('a=2') Maybe I am missing the point, but running |
@ivan-krukov the code you gave has no output, so I wouldn't expect to see any. When I run something that has an exception, I do see it: from jupyter_client import find_connection_file, BlockingKernelClient
kc = BlockingKernelClient(connection_file = find_connection_file())
kc.load_connection_file()
kc.execute_interactive('1/0') produces:
|
@minrk Thanks. |
Yes, the outputs are posted to IOPub, so they will appear in the console if that flag is specified, but not immediately. Since this was originally written against readline, we could only check for outputs after each prompt, and with prompt-toolkit it looks like we are now only checking on each execution. However, because prompt-toolkit has a proper eventloop, we should be able to handle it better than before. I opened jupyter/jupyter_console#105 to track this. |
Since 4.4 is out, I'll merge this soon unless people want more time to review. |
Redisplays output, returns reply.
Can be used in #184
Closes #176
cc @dsblank