use CFRunLoop directly in `ipython kernel --pylab osx` #809

Merged
merged 2 commits into from Oct 8, 2011

Projects

None yet

2 participants

@minrk
IPython member

CFRunLoop integration is now made via matplotlib.backend_macosx.TimerMac, rather than assuming Tk is native.

It will still fallback on Tk if matplotlib is < 1.1.0, which introduces the necessary Timer. This means that it still won't work on current EPD, which has X11-linked libtk and matplotlib 1.0.1, but at least it will display a warning explaining why.

Also removes caveat in docs that qtconsole doesn't work with native MacOSX, since it does on any normal (non-EPD) install.

So this will work in more places, but still not in the most common failure case (stock EPD) described in #640.

Yet another approach is to use PyObjC to hook into the eventloop directly, but that still doesn't solve the EPD problem, which doesn't ship with PyObjC (it probably should).

@minrk minrk use CFRunLoop directly in `ipython kernel --pylab osx`
via matplotlib.backend_macosx.TimerMac, rather than Tk

Fallback on Tk if matplotlib is < 1.1.0, which introduces the necessary Timer. This means that it still won't work on current EPD, which has X11-linked libtk and matplotlib 1.0.1,
but at least it will display a warning explaining why.

also remove caveat in docs that qtconsole doesn't work with native MacOSX, since it does on normal (non-EPD) installs.

So this will work in more places, but still not in most common failure case (stock EPD) described in #640.
5942e2a
@fperez fperez and 1 other commented on an outdated diff Sep 30, 2011
IPython/zmq/ipkernel.py
+ real_excepthook = sys.excepthook
+ def handle_int(etype, value, tb):
+ """don't let KeyboardInterrupts look like crashes"""
+ if etype is KeyboardInterrupt:
+ io.raw_print("KeyboardInterrupt caught in CFRunLoop")
+ else:
+ real_excepthook(etype, value, tb)
+
+ # add doi() as a Timer to the CFRunLoop
+ def doi():
+ # restore excepthook during IPython code
+ sys.excepthook = real_excepthook
+ self.do_one_iteration()
+ # and back:
+ sys.excepthook = handle_int
+ t = TimerMac(poll_interval)
@fperez
fperez Sep 30, 2011

I'd add a blank line after the end of the func definition for readability

@fperez fperez commented on the diff Sep 30, 2011
IPython/zmq/ipkernel.py
+ t.start()
+
+ # but still need a Poller for when there are no active windows,
+ # during which time mainloop() returns immediately
+ poller = zmq.Poller()
+ poller.register(self.shell_socket, zmq.POLLIN)
+
+ while True:
+ try:
+ # double nested try/except, to properly catch KeyboardInterrupt
+ # due to pyzmq Issue #130
+ try:
+ # don't let interrupts during mainloop invoke crash_handler:
+ sys.excepthook = handle_int
+ show.mainloop()
+ sys.excepthook = real_excepthook
@fperez
fperez Sep 30, 2011

What happens if there's an exception in the mainloop call? sys.excepthook won't get properly restored...

@minrk
minrk Oct 1, 2011

I'll stick the restore-excepthook in a finally clause, to make sure it is restored. Note that the special excepthook is identical to the original, save for handing keyboard interrupts, so there really isn't any difference. Either the error is a KeyboardInterrupt, and the loop is entered again, with no change, or it is anything else, which generates a crash report as usual, and exits.

Here's a question, though - should an exception in mainloop cause an IPython crash report? There is zero IPython code run in there, it's all matplotlib. Should a matplotlib bug be presented to the user as an IPython crash?

I worry that our crash reports are sometimes overzealous, often presenting far too much information, and ultimately being less instructive than a regular traceback.

@fperez
fperez Oct 1, 2011

Valid concern, and it seems we're producing more of them lately. They were originally so aggressive because they really were a way for us to debug an actual crash in the user's code. So they should only happen when IPython totally died.

But whenever we catch a normal exception (possibly from mpl or another tool), showtraceback() should be used instead. I'm not sure why we've ended up over time with our crash handler showing up so often.

@minrk
minrk Oct 1, 2011

Actually, I was wrong - mainloop includes calls to IPython's doi() via a timer, so all IPython code does end up within that call once a single plot window exists. And the KernelApp does not register a full crashhandler, it just registers FormattedTB, so you do just get a regular traceback instead of a crash. But if the crash_handler catches an error, it goes to the terminal, instead of being posted via PUB to frontends. This is true for all Kernels.

@minrk minrk ensure excepthook is restored in OSXKernel mainloop
also add some friendly whitespace per @fperez review
5175b0e
@minrk minrk merged commit 321d643 into ipython:master Oct 8, 2011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment