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: Run GUI eventloop while waiting for input #438
Conversation
We can't depend on |
@impact27 @ccordoba12 do we expect more PRs leading up to the Spyder release? |
Sure, I am not sure how you would prefer to do that?
I don't expect more PRs but I was not really expecting this one. |
I just added a |
We could port some of the improvments from spyder-kernels into ipykernel. We don't need to do that before the Spyder release. These improvments are mostly ipdb related. I don't know if you would be willing to have a ipdb subclass for ipykernel but the advantages that this could bring would be:
|
@impact27, I don't how these improvements would work in the notebook, which also has support for That's the main issue you'll probably face when trying to move them upstream. |
@impact27, did you test this with the inline backend? If so, could you post a screenshot about it? |
(The screenshots are from qtconsole) |
Nice!! Thanks for posting them! Pinging @tacaswell about this one because he'll probably be very interested about this new development. |
And @SylvainCorlay could also be interested about this because I think the comms fix should allow using bqplot in debugging mode. |
ipykernel/kernelbase.py
Outdated
@@ -861,6 +862,13 @@ def raw_input(self, prompt=''): | |||
) | |||
|
|||
def _input_request(self, prompt, ident, parent, password=False): | |||
"""Send an input request to the frontend and wait for the reply.""" | |||
# matplotlib needs to be imported after app.launch_new_instance() | |||
try: |
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.
Better to do if 'matplotlib' in sys.modules: ...
as this will import matplotlib
if it is installed, but not yet imported and it is not the quickest import.
I am not stoked about importing a private module here, I would do something like
if 'matplotlib.pyplot' in sys.modules:
def flush():
import matplotlib.pyplot as plt
if plt.get_fignums():
plt.gcf().canvas.flush_events()
else:
flush = None
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 imported that as this is the same import made here:
from matplotlib._pylab_helpers import Gcf |
and here in
plt.pause
:https://github.com/matplotlib/matplotlib/blob/5e9c54e7c4feb296b387fadb56394ef67ac2927f/lib/matplotlib/pyplot.py#L307
I am not sure if there is a subtle difference between
Gcf.get_active().canvas.flush_events()
and plt.gcf().canvas.flush_events()
?
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.
Gcf
is not part of the public API (not withstanding the other usage in IPython).
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.
plt.gcf().canvas.flush_events()
creates a new figure if no figures are open. Is there any way to avoid that?
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.
plt.get_current_fig_manager()
has the same problem. It creates an active figure as a side effect.
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.
Sorry I missed the plt.get_fignums()
I am not really sure about this.
One thing we may want to add to the protocol though is the ability to have a timeout option to the input request so that it does not hang forever... |
Alternatively, this could just call
The problem I am trying to solve is to process messages while waiting for a Pdb prompt (ideally I would process all events that are normally processed while waiting for a regular prompt. My understanding is that an alternative way to do that is to run the asyncio loop somehow. I think that would require a large part of What message would actually be problematic to process here? Surely e.g. a completion request would be fine. I am trying to understand the potential problems as this is what we already do in spyder. In Now that I think about it, maybe a better alternative would be to run the debugger (or even files) in another process. Threads wouldn't work because matplotlib and other libraries want to be on the main thread. This would also solve the problem of asyncio not running (see jupyter/notebook#3397) but I think I am getting off topic...
Well if I am debugging something and go for lunch, I would expect to be able to resume debugging when I get back. Therefore I don't really see any reasonable timeout time. |
Is it possible to send a message with low priority? Like saying explicitely: It is ok to process other messages while this message is being processed? |
If comm messages are queued, then |
We need to think about possible race conditions. It may as well be ok. |
I guess the problem is that they will start processing in order, but might not finish processing in order. |
comm messages are just shell messages and are queued with other shell messages. If code is written with the assumption the input() is blocking, I presume this may as well break. |
As an alternative, I created #439 that would allow to change this behaviour easily. |
The problem at the bottom here is that for the GUI to "look alive" you need to spin the input hook or is someother way give the GUI event loop some time to process it's events (user input, redrawing, etc). Calling Way more details than anyone really wants at matplotlib/matplotlib#4779 |
ipython/ipython#5026 this popped up in my feed today and seems at least a bit relevant. |
I think ipython/ipython#5026 is a front end problem: the kernel issues a input request but the frontend doesn’t know what to do with it. This PR is not really related as running the GUI event loop doesn’t really help here. |
Mostly race conditions / failure modes in |
There is a thread lock now so this shouldn’t happen
|
except Exception: | ||
self.log.error("Invalid Message", exc_info=True) | ||
return | ||
self._stdin_msg = msg |
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.
Don't we need to lock here too?
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.
The messages are processed by a single thread so a lock would not be useful here.
threading._MainThread) | ||
if is_main_thread and self.eventloop: | ||
self.eventloop(self) | ||
return self._stdin_msg |
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.
and possibly here?
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.
_input_request_loop_step
is called from _wait_input_request_reply
which is protected by a lock in _input_request
, so I don't think a lock here would do anything.
I have added an option to enable this behaviour. I think it should be off by default for the following reason:
Should matplotlib be called explicitly to avoid this problem as was the case earlier in this PR? |
I opened #465 as an alternative that only updates matplotlib. |
I tried to add a |
0258ae9
to
99e3d4f
Compare
37ecad1
to
bf3cb03
Compare
Hi, I'm trying to use your updates to plot while debugging. I've installed it by doing the following.
I then start ipython and attempt to run your magic command
which returns the following.
If I try plot anyways. I have to run Can you give me an idea of what I'm doing wrong? Also wondering if there's a timeline for integrating this back to master? Lack of this functionality is a serious productivity killer for those of us who are coming from MATLAB. |
And check if the file is indeed the one you are expecting. About the timeline, the problem is that there is a trade-off here: If this is implemented, any app that uses an event loop (such as matplotlib) can keep running while the debugger is stopped. This is good because you can plot things but bad if you are trying to debug a gui app. This trade-off means it is not trivial to merge this PR. My idea was to leave this behaviour disabled by default but give an option to enable it (with the Another solution for this problem would be to modify Pdb so it uses a custom method for blocking the code. This would be quite a big change and a lot of work. |
Thanks for getting back to me so quickly. This is indeed the directory I cloned your respo into. The head of my clone is pointed to 'd16d6e4e40f68f384d57a1a4458d4ef848e4ceb5' which is from about a week ago. Should I be using this commit? Introducing the magic command to allow the gui event loop to keep running seems like a very reasonable solution to me given the amount of work it would be to modify pdb. I guess I can't see what about this solution would prevent it from being merged. |
I am not convinced this is the right approach. This might introduce unwanted side effects.
and then running eventloop from this function. This means the eventloop is only run when called from pdb instead of any input calls. The main thing that would be nice to have is a way of interrupting the eventloop. The workaround I found is to send a dummy message on the shell channel, but having a |
I am going to close this for now as I am not convinced this is the right approach. What I am afraid of is that someone will use input() to try and block the execution. If the eventloop is still running, some code could be executed by the eventloop. This could make it hard to debug qt apps for example. Of course this is only an edge case but there should be at least an easy way of disabling this. |
I recall that this problem wasn't happening with IPython 7.7. Actually, I also remember that I didn't update IPython for a very long time just to avoid braking the integration between ipdb and Matplotlib. |
I just want to add that the issue with Matplotlib is not happening with pdb, only with ipdb. |
While waiting for the PY2
raw_input
/ PY3input
function, ipykernel is now completely blocked.This is a problem for example while debugging. (Pdb uses
input
to get a command).This PR would allow two new things to happen while waiting an input:
self.do_one_iteration(
) would give a chance to processcomms
messages.manager.canvas.flush_events()
would give a change to any matplotlib backend to update.