Is there a way for an application to signal to ipython that it should exit gracefully? My use case is installing a signal handler to force IPython to exit.
I would like something like this to work:
In [1]: import sys, signal
In [2]: def exit_now(*args): sys.exit()
In [3]: signal.signal(signal.SIGALRM, exit_now)
Out[3]: 0
In [4]: signal.alarm(2)
Out[4]: 0
In [5]:
It used to be that forcing an EOF on stdin would work, but prompt_toolkit does not handle that correctly anymore. See also prompt-toolkit/python-prompt-toolkit#392
get_ipython().ask_exit() is the official way, I think.
We've always caught SystemExit, and I presume that's because people sometimes %run code which uses sys.exit() to finish, while the user doesn't want to be booted out of the interactive prompt.
We've always caught SystemExit, and I presume that's because people sometimes %run code which uses sys.exit() to finish, while the user doesn't want to be booted out of the interactive prompt.
I understand that and I am not asking that to change.
get_ipython().ask_exit() is the official way, I think.
That works on the IPython part, but not on the prompt_toolkit part. Executing ask_exit() while prompt_toolkit is waiting for a prompt will not cause IPython to exit. So this looks more like a prompt_toolkit problem and not an IPython problem.
That's expected, I think - ask_exit sets a flag which we only check in our interaction loop, not while PT is running the event loop for the prompt. However, I'm a bit confused about why you want to exit programmatically while there's a prompt displayed.
Maybe we should just be installing a signal handler for SIGTERM. How would that interact with what you're trying to do?
How would that interact with what you're trying to do?
It is essentially exactly what I'm trying to do, except that I also care about plain Python (not IPython) prompts. And I want this both for SIGHUP and SIGTERM.
Neither of those works. Both of them do not react immediately, but only after pressing some key. This is logical, because none of the lines of code that you write would cause the input prompt to be aborted.
With ip.pt_cli.set_return_value(None), you even get an exception:
Traceback (most recent call last):
File "/usr/local/src/sage-config/local/bin/ipython", line 5, in <module>
start_ipython()
File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/__init__.py", line 119, in start_ipython
return launch_new_instance(argv=argv, **kwargs)
File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/traitlets/config/application.py", line 596, in launch_instance
app.start()
File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/terminal/ipapp.py", line 348, in start
self.shell.mainloop()
File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/terminal/interactiveshell.py", line 402, in mainloop
self.interact()
File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/terminal/interactiveshell.py", line 385, in interact
code = self.prompt_for_code()
File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/terminal/interactiveshell.py", line 322, in prompt_for_code
return document.text
AttributeError: 'NoneType' object has no attribute 'text'
In that case I think it's up to the implementation of the PT event loop.
I remain puzzled as to how you're arriving at this scenario, though. AIUI, signals are handled in the main thread, but if that's running the IPython interface, where's your code running? Or are you running IPython in a background thread for some reason?
Activity
takluyver commented on Sep 13, 2016
get_ipython().ask_exit() is the official way, I think.
We've always caught SystemExit, and I presume that's because people sometimes %run code which uses sys.exit() to finish, while the user doesn't want to be booted out of the interactive prompt.
minrk commented on Sep 13, 2016
Shouldn't we be doing a graceful exit on SIGTERM? I know we aren't, but it seems like we should.
jdemeyer commented on Sep 13, 2016
That is what I am trying to do in https://github.com/sagemath/cysignals. However, the trick which used to work with IPython 4 no longer works now.
jdemeyer commented on Sep 13, 2016
I understand that and I am not asking that to change.
jdemeyer commented on Sep 13, 2016
That works on the IPython part, but not on the prompt_toolkit part. Executing ask_exit() while prompt_toolkit is waiting for a prompt will not cause IPython to exit. So this looks more like a prompt_toolkit problem and not an IPython problem.
takluyver commented on Sep 13, 2016
That's expected, I think - ask_exit sets a flag which we only check in our interaction loop, not while PT is running the event loop for the prompt. However, I'm a bit confused about why you want to exit programmatically while there's a prompt displayed.
Maybe we should just be installing a signal handler for SIGTERM. How would that interact with what you're trying to do?
jdemeyer commented on Sep 13, 2016
The whole point of this issue and prompt-toolkit/python-prompt-toolkit#392 is that it's not so simple to "just" do that.
It is essentially exactly what I'm trying to do, except that I also care about plain Python (not IPython) prompts. And I want this both for SIGHUP and SIGTERM.
takluyver commented on Sep 13, 2016
Does this work:
takluyver commented on Sep 13, 2016
Looking at the code, I suspect that will give you the 'are you sure' prompt. If so, try changing the last line to:
jdemeyer commented on Sep 13, 2016
Neither of those works. Both of them do not react immediately, but only after pressing some key. This is logical, because none of the lines of code that you write would cause the input prompt to be aborted.
With ip.pt_cli.set_return_value(None), you even get an exception:
Traceback (most recent call last): File "/usr/local/src/sage-config/local/bin/ipython", line 5, in <module> start_ipython() File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/__init__.py", line 119, in start_ipython return launch_new_instance(argv=argv, **kwargs) File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/traitlets/config/application.py", line 596, in launch_instance app.start() File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/terminal/ipapp.py", line 348, in start self.shell.mainloop() File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/terminal/interactiveshell.py", line 402, in mainloop self.interact() File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/terminal/interactiveshell.py", line 385, in interact code = self.prompt_for_code() File "/usr/local/src/sage-config/local/lib/python2.7/site-packages/IPython/terminal/interactiveshell.py", line 322, in prompt_for_code return document.text AttributeError: 'NoneType' object has no attribute 'text'takluyver commented on Sep 13, 2016
In that case I think it's up to the implementation of the PT event loop.
I remain puzzled as to how you're arriving at this scenario, though. AIUI, signals are handled in the main thread, but if that's running the IPython interface, where's your code running? Or are you running IPython in a background thread for some reason?
jdemeyer commented on Sep 13, 2016
I don't understand your question:
Which "scenario"?
Which "code"?
takluyver commented on Sep 13, 2016
The scenario where an IPython prompt is displayed and some other code wants to exit it programmatically.
The code that receives the signal and wishes to quit the prompt.
Maybe it's coming from another event loop using the inputhook integration?
jdemeyer commented on Sep 13, 2016
The scenario is really exactly what you described. I want to exit IPython gracefully when receiving a SIGTERM signal.
I wasn't even aware of multiple threads. Anyway, my code is running in whatever thread is handling the signal.
takluyver commented on Sep 13, 2016
Right, signal handlers preempt regular execution. Don't mind me...
Exit terminal interface gracefully on signals
takluyver commented on Jan 19, 2017
#10185 is my attempt to deal with this.
ivanov commented on May 17, 2017
@jdemeyer I just want to mention that if you would like the old (IPython <= 4.x) readline interface, it is now available separately as rlipython.
Right now, it only works in IPython 6.0 (Python 3), but it will also work in Python 2 with the next IPython 5 release (5.4).
techtonik commented on Jul 6, 2018
From prompt-toolkit/python-prompt-toolkit#392 (comment) prompt toolkit allows this: