##// END OF EJS Templates
use CFRunLoop directly in `ipython kernel --pylab osx`...
MinRK -
Show More
@@ -1,702 +1,769 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Standard library imports.
19 19 import __builtin__
20 20 import atexit
21 21 import sys
22 22 import time
23 23 import traceback
24 24 import logging
25 25 # System library imports.
26 26 import zmq
27 27
28 28 # Local imports.
29 29 from IPython.config.configurable import Configurable
30 30 from IPython.config.application import boolean_flag
31 31 from IPython.core.application import ProfileDir
32 32 from IPython.core.shellapp import (
33 33 InteractiveShellApp, shell_flags, shell_aliases
34 34 )
35 35 from IPython.utils import io
36 36 from IPython.utils import py3compat
37 37 from IPython.utils.jsonutil import json_clean
38 38 from IPython.lib import pylabtools
39 39 from IPython.utils.traitlets import (
40 40 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
41 41 )
42 42
43 43 from entry_point import base_launch_kernel
44 44 from kernelapp import KernelApp, kernel_flags, kernel_aliases
45 45 from iostream import OutStream
46 46 from session import Session, Message
47 47 from zmqshell import ZMQInteractiveShell
48 48
49 49
50 50 #-----------------------------------------------------------------------------
51 51 # Main kernel class
52 52 #-----------------------------------------------------------------------------
53 53
54 54 class Kernel(Configurable):
55 55
56 56 #---------------------------------------------------------------------------
57 57 # Kernel interface
58 58 #---------------------------------------------------------------------------
59 59
60 60 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
61 61 session = Instance(Session)
62 62 shell_socket = Instance('zmq.Socket')
63 63 iopub_socket = Instance('zmq.Socket')
64 64 stdin_socket = Instance('zmq.Socket')
65 65 log = Instance(logging.Logger)
66 66
67 67 # Private interface
68 68
69 69 # Time to sleep after flushing the stdout/err buffers in each execute
70 70 # cycle. While this introduces a hard limit on the minimal latency of the
71 71 # execute cycle, it helps prevent output synchronization problems for
72 72 # clients.
73 73 # Units are in seconds. The minimum zmq latency on local host is probably
74 74 # ~150 microseconds, set this to 500us for now. We may need to increase it
75 75 # a little if it's not enough after more interactive testing.
76 76 _execute_sleep = Float(0.0005, config=True)
77 77
78 78 # Frequency of the kernel's event loop.
79 79 # Units are in seconds, kernel subclasses for GUI toolkits may need to
80 80 # adapt to milliseconds.
81 81 _poll_interval = Float(0.05, config=True)
82 82
83 83 # If the shutdown was requested over the network, we leave here the
84 84 # necessary reply message so it can be sent by our registered atexit
85 85 # handler. This ensures that the reply is only sent to clients truly at
86 86 # the end of our shutdown process (which happens after the underlying
87 87 # IPython shell's own shutdown).
88 88 _shutdown_message = None
89 89
90 90 # This is a dict of port number that the kernel is listening on. It is set
91 91 # by record_ports and used by connect_request.
92 92 _recorded_ports = Dict()
93 93
94 94
95 95
96 96 def __init__(self, **kwargs):
97 97 super(Kernel, self).__init__(**kwargs)
98 98
99 99 # Before we even start up the shell, register *first* our exit handlers
100 100 # so they come before the shell's
101 101 atexit.register(self._at_shutdown)
102 102
103 103 # Initialize the InteractiveShell subclass
104 104 self.shell = ZMQInteractiveShell.instance(config=self.config)
105 105 self.shell.displayhook.session = self.session
106 106 self.shell.displayhook.pub_socket = self.iopub_socket
107 107 self.shell.display_pub.session = self.session
108 108 self.shell.display_pub.pub_socket = self.iopub_socket
109 109
110 110 # TMP - hack while developing
111 111 self.shell._reply_content = None
112 112
113 113 # Build dict of handlers for message types
114 114 msg_types = [ 'execute_request', 'complete_request',
115 115 'object_info_request', 'history_request',
116 116 'connect_request', 'shutdown_request']
117 117 self.handlers = {}
118 118 for msg_type in msg_types:
119 119 self.handlers[msg_type] = getattr(self, msg_type)
120 120
121 121 def do_one_iteration(self):
122 122 """Do one iteration of the kernel's evaluation loop.
123 123 """
124 124 try:
125 125 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
126 126 except Exception:
127 127 self.log.warn("Invalid Message:", exc_info=True)
128 128 return
129 129 if msg is None:
130 130 return
131 131
132 132 msg_type = msg['header']['msg_type']
133 133
134 134 # This assert will raise in versions of zeromq 2.0.7 and lesser.
135 135 # We now require 2.0.8 or above, so we can uncomment for safety.
136 136 # print(ident,msg, file=sys.__stdout__)
137 137 assert ident is not None, "Missing message part."
138 138
139 139 # Print some info about this message and leave a '--->' marker, so it's
140 140 # easier to trace visually the message chain when debugging. Each
141 141 # handler prints its message at the end.
142 142 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
143 143 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
144 144
145 145 # Find and call actual handler for message
146 146 handler = self.handlers.get(msg_type, None)
147 147 if handler is None:
148 148 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
149 149 else:
150 150 handler(ident, msg)
151 151
152 152 # Check whether we should exit, in case the incoming message set the
153 153 # exit flag on
154 154 if self.shell.exit_now:
155 155 self.log.debug('\nExiting IPython kernel...')
156 156 # We do a normal, clean exit, which allows any actions registered
157 157 # via atexit (such as history saving) to take place.
158 158 sys.exit(0)
159 159
160 160
161 161 def start(self):
162 162 """ Start the kernel main loop.
163 163 """
164 164 poller = zmq.Poller()
165 165 poller.register(self.shell_socket, zmq.POLLIN)
166 166 while True:
167 167 try:
168 168 # scale by extra factor of 10, because there is no
169 169 # reason for this to be anything less than ~ 0.1s
170 170 # since it is a real poller and will respond
171 171 # to events immediately
172 172
173 173 # double nested try/except, to properly catch KeyboardInterrupt
174 174 # due to pyzmq Issue #130
175 175 try:
176 176 poller.poll(10*1000*self._poll_interval)
177 177 self.do_one_iteration()
178 178 except:
179 179 raise
180 180 except KeyboardInterrupt:
181 181 # Ctrl-C shouldn't crash the kernel
182 182 io.raw_print("KeyboardInterrupt caught in kernel")
183 183
184 184 def record_ports(self, ports):
185 185 """Record the ports that this kernel is using.
186 186
187 187 The creator of the Kernel instance must call this methods if they
188 188 want the :meth:`connect_request` method to return the port numbers.
189 189 """
190 190 self._recorded_ports = ports
191 191
192 192 #---------------------------------------------------------------------------
193 193 # Kernel request handlers
194 194 #---------------------------------------------------------------------------
195 195
196 196 def _publish_pyin(self, code, parent):
197 197 """Publish the code request on the pyin stream."""
198 198
199 199 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
200 200
201 201 def execute_request(self, ident, parent):
202 202
203 203 status_msg = self.session.send(self.iopub_socket,
204 204 u'status',
205 205 {u'execution_state':u'busy'},
206 206 parent=parent
207 207 )
208 208
209 209 try:
210 210 content = parent[u'content']
211 211 code = content[u'code']
212 212 silent = content[u'silent']
213 213 except:
214 214 self.log.error("Got bad msg: ")
215 215 self.log.error(str(Message(parent)))
216 216 return
217 217
218 218 shell = self.shell # we'll need this a lot here
219 219
220 220 # Replace raw_input. Note that is not sufficient to replace
221 221 # raw_input in the user namespace.
222 222 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
223 223 if py3compat.PY3:
224 224 __builtin__.input = raw_input
225 225 else:
226 226 __builtin__.raw_input = raw_input
227 227
228 228 # Set the parent message of the display hook and out streams.
229 229 shell.displayhook.set_parent(parent)
230 230 shell.display_pub.set_parent(parent)
231 231 sys.stdout.set_parent(parent)
232 232 sys.stderr.set_parent(parent)
233 233
234 234 # Re-broadcast our input for the benefit of listening clients, and
235 235 # start computing output
236 236 if not silent:
237 237 self._publish_pyin(code, parent)
238 238
239 239 reply_content = {}
240 240 try:
241 241 if silent:
242 242 # run_code uses 'exec' mode, so no displayhook will fire, and it
243 243 # doesn't call logging or history manipulations. Print
244 244 # statements in that code will obviously still execute.
245 245 shell.run_code(code)
246 246 else:
247 247 # FIXME: the shell calls the exception handler itself.
248 248 shell.run_cell(code)
249 249 except:
250 250 status = u'error'
251 251 # FIXME: this code right now isn't being used yet by default,
252 252 # because the run_cell() call above directly fires off exception
253 253 # reporting. This code, therefore, is only active in the scenario
254 254 # where runlines itself has an unhandled exception. We need to
255 255 # uniformize this, for all exception construction to come from a
256 256 # single location in the codbase.
257 257 etype, evalue, tb = sys.exc_info()
258 258 tb_list = traceback.format_exception(etype, evalue, tb)
259 259 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
260 260 else:
261 261 status = u'ok'
262 262
263 263 reply_content[u'status'] = status
264 264
265 265 # Return the execution counter so clients can display prompts
266 266 reply_content['execution_count'] = shell.execution_count -1
267 267
268 268 # FIXME - fish exception info out of shell, possibly left there by
269 269 # runlines. We'll need to clean up this logic later.
270 270 if shell._reply_content is not None:
271 271 reply_content.update(shell._reply_content)
272 272 # reset after use
273 273 shell._reply_content = None
274 274
275 275 # At this point, we can tell whether the main code execution succeeded
276 276 # or not. If it did, we proceed to evaluate user_variables/expressions
277 277 if reply_content['status'] == 'ok':
278 278 reply_content[u'user_variables'] = \
279 279 shell.user_variables(content[u'user_variables'])
280 280 reply_content[u'user_expressions'] = \
281 281 shell.user_expressions(content[u'user_expressions'])
282 282 else:
283 283 # If there was an error, don't even try to compute variables or
284 284 # expressions
285 285 reply_content[u'user_variables'] = {}
286 286 reply_content[u'user_expressions'] = {}
287 287
288 288 # Payloads should be retrieved regardless of outcome, so we can both
289 289 # recover partial output (that could have been generated early in a
290 290 # block, before an error) and clear the payload system always.
291 291 reply_content[u'payload'] = shell.payload_manager.read_payload()
292 292 # Be agressive about clearing the payload because we don't want
293 293 # it to sit in memory until the next execute_request comes in.
294 294 shell.payload_manager.clear_payload()
295 295
296 296 # Flush output before sending the reply.
297 297 sys.stdout.flush()
298 298 sys.stderr.flush()
299 299 # FIXME: on rare occasions, the flush doesn't seem to make it to the
300 300 # clients... This seems to mitigate the problem, but we definitely need
301 301 # to better understand what's going on.
302 302 if self._execute_sleep:
303 303 time.sleep(self._execute_sleep)
304 304
305 305 # Send the reply.
306 306 reply_content = json_clean(reply_content)
307 307 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
308 308 reply_content, parent, ident=ident)
309 309 self.log.debug(str(reply_msg))
310 310
311 311 if reply_msg['content']['status'] == u'error':
312 312 self._abort_queue()
313 313
314 314 status_msg = self.session.send(self.iopub_socket,
315 315 u'status',
316 316 {u'execution_state':u'idle'},
317 317 parent=parent
318 318 )
319 319
320 320 def complete_request(self, ident, parent):
321 321 txt, matches = self._complete(parent)
322 322 matches = {'matches' : matches,
323 323 'matched_text' : txt,
324 324 'status' : 'ok'}
325 325 matches = json_clean(matches)
326 326 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
327 327 matches, parent, ident)
328 328 self.log.debug(str(completion_msg))
329 329
330 330 def object_info_request(self, ident, parent):
331 331 object_info = self.shell.object_inspect(parent['content']['oname'])
332 332 # Before we send this object over, we scrub it for JSON usage
333 333 oinfo = json_clean(object_info)
334 334 msg = self.session.send(self.shell_socket, 'object_info_reply',
335 335 oinfo, parent, ident)
336 336 self.log.debug(msg)
337 337
338 338 def history_request(self, ident, parent):
339 339 # We need to pull these out, as passing **kwargs doesn't work with
340 340 # unicode keys before Python 2.6.5.
341 341 hist_access_type = parent['content']['hist_access_type']
342 342 raw = parent['content']['raw']
343 343 output = parent['content']['output']
344 344 if hist_access_type == 'tail':
345 345 n = parent['content']['n']
346 346 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
347 347 include_latest=True)
348 348
349 349 elif hist_access_type == 'range':
350 350 session = parent['content']['session']
351 351 start = parent['content']['start']
352 352 stop = parent['content']['stop']
353 353 hist = self.shell.history_manager.get_range(session, start, stop,
354 354 raw=raw, output=output)
355 355
356 356 elif hist_access_type == 'search':
357 357 pattern = parent['content']['pattern']
358 358 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
359 359
360 360 else:
361 361 hist = []
362 362 content = {'history' : list(hist)}
363 363 content = json_clean(content)
364 364 msg = self.session.send(self.shell_socket, 'history_reply',
365 365 content, parent, ident)
366 366 self.log.debug(str(msg))
367 367
368 368 def connect_request(self, ident, parent):
369 369 if self._recorded_ports is not None:
370 370 content = self._recorded_ports.copy()
371 371 else:
372 372 content = {}
373 373 msg = self.session.send(self.shell_socket, 'connect_reply',
374 374 content, parent, ident)
375 375 self.log.debug(msg)
376 376
377 377 def shutdown_request(self, ident, parent):
378 378 self.shell.exit_now = True
379 379 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
380 380 sys.exit(0)
381 381
382 382 #---------------------------------------------------------------------------
383 383 # Protected interface
384 384 #---------------------------------------------------------------------------
385 385
386 386 def _abort_queue(self):
387 387 while True:
388 388 try:
389 389 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
390 390 except Exception:
391 391 self.log.warn("Invalid Message:", exc_info=True)
392 392 continue
393 393 if msg is None:
394 394 break
395 395 else:
396 396 assert ident is not None, \
397 397 "Unexpected missing message part."
398 398
399 399 self.log.debug("Aborting:\n"+str(Message(msg)))
400 400 msg_type = msg['header']['msg_type']
401 401 reply_type = msg_type.split('_')[0] + '_reply'
402 402 reply_msg = self.session.send(self.shell_socket, reply_type,
403 403 {'status' : 'aborted'}, msg, ident=ident)
404 404 self.log.debug(reply_msg)
405 405 # We need to wait a bit for requests to come in. This can probably
406 406 # be set shorter for true asynchronous clients.
407 407 time.sleep(0.1)
408 408
409 409 def _raw_input(self, prompt, ident, parent):
410 410 # Flush output before making the request.
411 411 sys.stderr.flush()
412 412 sys.stdout.flush()
413 413
414 414 # Send the input request.
415 415 content = json_clean(dict(prompt=prompt))
416 416 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
417 417
418 418 # Await a response.
419 419 while True:
420 420 try:
421 421 ident, reply = self.session.recv(self.stdin_socket, 0)
422 422 except Exception:
423 423 self.log.warn("Invalid Message:", exc_info=True)
424 424 else:
425 425 break
426 426 try:
427 427 value = reply['content']['value']
428 428 except:
429 429 self.log.error("Got bad raw_input reply: ")
430 430 self.log.error(str(Message(parent)))
431 431 value = ''
432 432 return value
433 433
434 434 def _complete(self, msg):
435 435 c = msg['content']
436 436 try:
437 437 cpos = int(c['cursor_pos'])
438 438 except:
439 439 # If we don't get something that we can convert to an integer, at
440 440 # least attempt the completion guessing the cursor is at the end of
441 441 # the text, if there's any, and otherwise of the line
442 442 cpos = len(c['text'])
443 443 if cpos==0:
444 444 cpos = len(c['line'])
445 445 return self.shell.complete(c['text'], c['line'], cpos)
446 446
447 447 def _object_info(self, context):
448 448 symbol, leftover = self._symbol_from_context(context)
449 449 if symbol is not None and not leftover:
450 450 doc = getattr(symbol, '__doc__', '')
451 451 else:
452 452 doc = ''
453 453 object_info = dict(docstring = doc)
454 454 return object_info
455 455
456 456 def _symbol_from_context(self, context):
457 457 if not context:
458 458 return None, context
459 459
460 460 base_symbol_string = context[0]
461 461 symbol = self.shell.user_ns.get(base_symbol_string, None)
462 462 if symbol is None:
463 463 symbol = __builtin__.__dict__.get(base_symbol_string, None)
464 464 if symbol is None:
465 465 return None, context
466 466
467 467 context = context[1:]
468 468 for i, name in enumerate(context):
469 469 new_symbol = getattr(symbol, name, None)
470 470 if new_symbol is None:
471 471 return symbol, context[i:]
472 472 else:
473 473 symbol = new_symbol
474 474
475 475 return symbol, []
476 476
477 477 def _at_shutdown(self):
478 478 """Actions taken at shutdown by the kernel, called by python's atexit.
479 479 """
480 480 # io.rprint("Kernel at_shutdown") # dbg
481 481 if self._shutdown_message is not None:
482 482 self.session.send(self.shell_socket, self._shutdown_message)
483 483 self.session.send(self.iopub_socket, self._shutdown_message)
484 484 self.log.debug(str(self._shutdown_message))
485 485 # A very short sleep to give zmq time to flush its message buffers
486 486 # before Python truly shuts down.
487 487 time.sleep(0.01)
488 488
489 489
490 490 class QtKernel(Kernel):
491 491 """A Kernel subclass with Qt support."""
492 492
493 493 def start(self):
494 494 """Start a kernel with QtPy4 event loop integration."""
495 495
496 496 from IPython.external.qt_for_kernel import QtCore
497 497 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
498 498
499 499 self.app = get_app_qt4([" "])
500 500 self.app.setQuitOnLastWindowClosed(False)
501 501 self.timer = QtCore.QTimer()
502 502 self.timer.timeout.connect(self.do_one_iteration)
503 503 # Units for the timer are in milliseconds
504 504 self.timer.start(1000*self._poll_interval)
505 505 start_event_loop_qt4(self.app)
506 506
507 507
508 508 class WxKernel(Kernel):
509 509 """A Kernel subclass with Wx support."""
510 510
511 511 def start(self):
512 512 """Start a kernel with wx event loop support."""
513 513
514 514 import wx
515 515 from IPython.lib.guisupport import start_event_loop_wx
516 516
517 517 doi = self.do_one_iteration
518 518 # Wx uses milliseconds
519 519 poll_interval = int(1000*self._poll_interval)
520 520
521 521 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
522 522 # We make the Frame hidden when we create it in the main app below.
523 523 class TimerFrame(wx.Frame):
524 524 def __init__(self, func):
525 525 wx.Frame.__init__(self, None, -1)
526 526 self.timer = wx.Timer(self)
527 527 # Units for the timer are in milliseconds
528 528 self.timer.Start(poll_interval)
529 529 self.Bind(wx.EVT_TIMER, self.on_timer)
530 530 self.func = func
531 531
532 532 def on_timer(self, event):
533 533 self.func()
534 534
535 535 # We need a custom wx.App to create our Frame subclass that has the
536 536 # wx.Timer to drive the ZMQ event loop.
537 537 class IPWxApp(wx.App):
538 538 def OnInit(self):
539 539 self.frame = TimerFrame(doi)
540 540 self.frame.Show(False)
541 541 return True
542 542
543 543 # The redirect=False here makes sure that wx doesn't replace
544 544 # sys.stdout/stderr with its own classes.
545 545 self.app = IPWxApp(redirect=False)
546 546 start_event_loop_wx(self.app)
547 547
548 548
549 549 class TkKernel(Kernel):
550 550 """A Kernel subclass with Tk support."""
551 551
552 552 def start(self):
553 553 """Start a Tk enabled event loop."""
554 554
555 555 import Tkinter
556 556 doi = self.do_one_iteration
557 557 # Tk uses milliseconds
558 558 poll_interval = int(1000*self._poll_interval)
559 559 # For Tkinter, we create a Tk object and call its withdraw method.
560 560 class Timer(object):
561 561 def __init__(self, func):
562 562 self.app = Tkinter.Tk()
563 563 self.app.withdraw()
564 564 self.func = func
565 565
566 566 def on_timer(self):
567 567 self.func()
568 568 self.app.after(poll_interval, self.on_timer)
569 569
570 570 def start(self):
571 571 self.on_timer() # Call it once to get things going.
572 572 self.app.mainloop()
573 573
574 574 self.timer = Timer(doi)
575 575 self.timer.start()
576 576
577 577
578 578 class GTKKernel(Kernel):
579 579 """A Kernel subclass with GTK support."""
580 580
581 581 def start(self):
582 582 """Start the kernel, coordinating with the GTK event loop"""
583 583 from .gui.gtkembed import GTKEmbed
584 584
585 585 gtk_kernel = GTKEmbed(self)
586 586 gtk_kernel.start()
587 587
588 588
589 class OSXKernel(TkKernel):
590 """A Kernel subclass with Cocoa support via the matplotlib OSX backend."""
591
592 def start(self):
593 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
594 via the matplotlib MacOSX backend.
595 """
596 import matplotlib
597 if matplotlib.__version__ < '1.1.0':
598 self.log.warn(
599 "MacOSX backend in matplotlib %s doesn't have a Timer, "
600 "falling back on Tk for CFRunLoop integration. Note that "
601 "even this won't work if Tk is linked against X11 instead of "
602 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
603 "you must use matplotlib >= 1.1.0, or a native libtk."
604 )
605 return TkKernel.start(self)
606
607 from matplotlib.backends.backend_macosx import TimerMac, show
608
609 # scale interval for sec->ms
610 poll_interval = int(1000*self._poll_interval)
611
612 real_excepthook = sys.excepthook
613 def handle_int(etype, value, tb):
614 """don't let KeyboardInterrupts look like crashes"""
615 if etype is KeyboardInterrupt:
616 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
617 else:
618 real_excepthook(etype, value, tb)
619
620 # add doi() as a Timer to the CFRunLoop
621 def doi():
622 # restore excepthook during IPython code
623 sys.excepthook = real_excepthook
624 self.do_one_iteration()
625 # and back:
626 sys.excepthook = handle_int
627 t = TimerMac(poll_interval)
628 t.add_callback(doi)
629 t.start()
630
631 # but still need a Poller for when there are no active windows,
632 # during which time mainloop() returns immediately
633 poller = zmq.Poller()
634 poller.register(self.shell_socket, zmq.POLLIN)
635
636 while True:
637 try:
638 # double nested try/except, to properly catch KeyboardInterrupt
639 # due to pyzmq Issue #130
640 try:
641 # don't let interrupts during mainloop invoke crash_handler:
642 sys.excepthook = handle_int
643 show.mainloop()
644 sys.excepthook = real_excepthook
645 # use poller if mainloop returned (no windows)
646 # scale by extra factor of 10, since it's a real poll
647 poller.poll(10*poll_interval)
648 self.do_one_iteration()
649 except:
650 raise
651 except KeyboardInterrupt:
652 # Ctrl-C shouldn't crash the kernel
653 io.raw_print("KeyboardInterrupt caught in kernel")
654
655
589 656 #-----------------------------------------------------------------------------
590 657 # Aliases and Flags for the IPKernelApp
591 658 #-----------------------------------------------------------------------------
592 659
593 660 flags = dict(kernel_flags)
594 661 flags.update(shell_flags)
595 662
596 663 addflag = lambda *args: flags.update(boolean_flag(*args))
597 664
598 665 flags['pylab'] = (
599 666 {'IPKernelApp' : {'pylab' : 'auto'}},
600 667 """Pre-load matplotlib and numpy for interactive use with
601 668 the default matplotlib backend."""
602 669 )
603 670
604 671 aliases = dict(kernel_aliases)
605 672 aliases.update(shell_aliases)
606 673
607 674 # it's possible we don't want short aliases for *all* of these:
608 675 aliases.update(dict(
609 676 pylab='IPKernelApp.pylab',
610 677 ))
611 678
612 679 #-----------------------------------------------------------------------------
613 680 # The IPKernelApp class
614 681 #-----------------------------------------------------------------------------
615 682
616 683 class IPKernelApp(KernelApp, InteractiveShellApp):
617 684 name = 'ipkernel'
618 685
619 686 aliases = Dict(aliases)
620 687 flags = Dict(flags)
621 688 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
622 689 # configurables
623 690 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
624 691 config=True,
625 692 help="""Pre-load matplotlib and numpy for interactive use,
626 693 selecting a particular matplotlib backend and loop integration.
627 694 """
628 695 )
629 696 def initialize(self, argv=None):
630 697 super(IPKernelApp, self).initialize(argv)
631 698 self.init_shell()
632 699 self.init_extensions()
633 700 self.init_code()
634 701
635 702 def init_kernel(self):
636 703 kernel_factory = Kernel
637 704
638 705 kernel_map = {
639 706 'qt' : QtKernel,
640 707 'qt4': QtKernel,
641 708 'inline': Kernel,
642 'osx': TkKernel,
709 'osx': OSXKernel,
643 710 'wx' : WxKernel,
644 711 'tk' : TkKernel,
645 712 'gtk': GTKKernel,
646 713 }
647 714
648 715 if self.pylab:
649 716 key = None if self.pylab == 'auto' else self.pylab
650 717 gui, backend = pylabtools.find_gui_and_backend(key)
651 718 kernel_factory = kernel_map.get(gui)
652 719 if kernel_factory is None:
653 720 raise ValueError('GUI is not supported: %r' % gui)
654 721 pylabtools.activate_matplotlib(backend)
655 722
656 723 kernel = kernel_factory(config=self.config, session=self.session,
657 724 shell_socket=self.shell_socket,
658 725 iopub_socket=self.iopub_socket,
659 726 stdin_socket=self.stdin_socket,
660 727 log=self.log
661 728 )
662 729 self.kernel = kernel
663 730 kernel.record_ports(self.ports)
664 731
665 732 if self.pylab:
666 733 import_all = self.pylab_import_all
667 734 pylabtools.import_pylab(kernel.shell.user_ns, backend, import_all,
668 735 shell=kernel.shell)
669 736
670 737 def init_shell(self):
671 738 self.shell = self.kernel.shell
672 739
673 740
674 741 #-----------------------------------------------------------------------------
675 742 # Kernel main and launch functions
676 743 #-----------------------------------------------------------------------------
677 744
678 745 def launch_kernel(*args, **kwargs):
679 746 """Launches a localhost IPython kernel, binding to the specified ports.
680 747
681 748 This function simply calls entry_point.base_launch_kernel with the right first
682 749 command to start an ipkernel. See base_launch_kernel for arguments.
683 750
684 751 Returns
685 752 -------
686 753 A tuple of form:
687 754 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
688 755 where kernel_process is a Popen object and the ports are integers.
689 756 """
690 757 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
691 758 *args, **kwargs)
692 759
693 760
694 761 def main():
695 762 """Run an IPKernel as an application"""
696 763 app = IPKernelApp.instance()
697 764 app.initialize()
698 765 app.start()
699 766
700 767
701 768 if __name__ == '__main__':
702 769 main()
@@ -1,490 +1,490 b''
1 1 .. _qtconsole:
2 2
3 3 =========================
4 4 A Qt Console for IPython
5 5 =========================
6 6
7 7 We now have a version of IPython, using the new two-process :ref:`ZeroMQ Kernel
8 8 <ipythonzmq>`, running in a PyQt_ GUI. This is a very lightweight widget that
9 9 largely feels like a terminal, but provides a number of enhancements only
10 10 possible in a GUI, such as inline figures, proper multiline editing with syntax
11 11 highlighting, graphical calltips, and much more.
12 12
13 13 .. figure:: ../_static/qtconsole.png
14 14 :width: 400px
15 15 :alt: IPython Qt console with embedded plots
16 16 :align: center
17 17 :target: ../_static/qtconsole.png
18 18
19 19 The Qt console for IPython, using inline matplotlib plots.
20 20
21 21 To get acquainted with the Qt console, type `%guiref` to see a quick
22 22 introduction of its main features.
23 23
24 24 The Qt frontend has hand-coded emacs-style bindings for text navigation. This
25 25 is not yet configurable.
26 26
27 27 .. tip::
28 28
29 29 Since the Qt console tries hard to behave like a terminal, by default it
30 30 immediately executes single lines of input that are complete. If you want
31 31 to force multiline input, hit :kbd:`Ctrl-Enter` at the end of the first line
32 32 instead of :kbd:`Enter`, and it will open a new line for input. At any
33 33 point in a multiline block, you can force its execution (without having to
34 34 go to the bottom) with :kbd:`Shift-Enter`.
35 35
36 36 ``%loadpy``
37 37 ===========
38 38
39 39 The new ``%loadpy`` magic takes any python script (must end in '.py'), and
40 40 pastes its contents as your next input, so you can edit it before
41 41 executing. The script may be on your machine, but you can also specify a url,
42 42 and it will download the script from the web. This is particularly useful for
43 43 playing with examples from documentation, such as matplotlib.
44 44
45 45 .. sourcecode:: ipython
46 46
47 47 In [6]: %loadpy http://matplotlib.sourceforge.net/plot_directive/mpl_examples/mplot3d/contour3d_demo.py
48 48
49 49 In [7]: from mpl_toolkits.mplot3d import axes3d
50 50 ...: import matplotlib.pyplot as plt
51 51 ...:
52 52 ...: fig = plt.figure()
53 53 ...: ax = fig.add_subplot(111, projection='3d')
54 54 ...: X, Y, Z = axes3d.get_test_data(0.05)
55 55 ...: cset = ax.contour(X, Y, Z)
56 56 ...: ax.clabel(cset, fontsize=9, inline=1)
57 57 ...:
58 58 ...: plt.show()
59 59
60 60 Pylab
61 61 =====
62 62
63 63 One of the most exciting features of the new console is embedded matplotlib
64 figures. You can use any standard matplotlib GUI backend (Except native MacOSX)
64 figures. You can use any standard matplotlib GUI backend
65 65 to draw the figures, and since there is now a two-process model, there is no
66 66 longer a conflict between user input and the drawing eventloop.
67 67
68 68 .. image:: figs/besselj.png
69 69 :width: 519px
70 70
71 71 .. display:
72 72
73 73 :func:`display`
74 74 ***************
75 75
76 76 An additional function, :func:`display`, will be added to the global namespace
77 77 if you specify the ``--pylab`` option at the command line. The IPython display
78 78 system provides a mechanism for specifying PNG or SVG (and more)
79 79 representations of objects for GUI frontends. By default, IPython registers
80 80 convenient PNG and SVG renderers for matplotlib figures, so you can embed them
81 81 in your document by calling :func:`display` on one or more of them. This is
82 82 especially useful for saving_ your work.
83 83
84 84 .. sourcecode:: ipython
85 85
86 86 In [5]: plot(range(5)) # plots in the matplotlib window
87 87
88 88 In [6]: display(gcf()) # embeds the current figure in the qtconsole
89 89
90 90 In [7]: display(*getfigs()) # embeds all active figures in the qtconsole
91 91
92 92 If you have a reference to a matplotlib figure object, you can always display
93 93 that specific figure:
94 94
95 95 .. sourcecode:: ipython
96 96
97 97 In [1]: f = figure()
98 98
99 99 In [2]: plot(rand(100))
100 100 Out[2]: [<matplotlib.lines.Line2D at 0x7fc6ac03dd90>]
101 101
102 102 In [3]: display(f)
103 103
104 104 # Plot is shown here
105 105
106 106 In [4]: title('A title')
107 107 Out[4]: <matplotlib.text.Text at 0x7fc6ac023450>
108 108
109 109 In [5]: display(f)
110 110
111 111 # Updated plot with title is shown here.
112 112
113 113 .. _inline:
114 114
115 115 ``--pylab=inline``
116 116 ******************
117 117
118 118 If you want to have all of your figures embedded in your session, instead of
119 119 calling :func:`display`, you can specify ``--pylab=inline`` when you start the
120 120 console, and each time you make a plot, it will show up in your document, as if
121 121 you had called :func:`display(fig)`.
122 122
123 123
124 124 .. _saving:
125 125
126 126 Saving and Printing
127 127 ===================
128 128
129 129 IPythonQt has the ability to save your current session, as either HTML or
130 130 XHTML. If you have been using :func:`display` or inline_ pylab, your figures
131 131 will be PNG in HTML, or inlined as SVG in XHTML. PNG images have the option to
132 132 be either in an external folder, as in many browsers' "Webpage, Complete"
133 133 option, or inlined as well, for a larger, but more portable file.
134 134
135 135 The widget also exposes the ability to print directly, via the default print
136 136 shortcut or context menu.
137 137
138 138
139 139 .. Note::
140 140
141 141 Saving is only available to richtext Qt widgets, which are used by default,
142 142 but if you pass the ``--plain`` flag, saving will not be available to you.
143 143
144 144
145 145 See these examples of :download:`png/html<figs/jn.html>` and
146 146 :download:`svg/xhtml <figs/jn.xhtml>` output. Note that syntax highlighting
147 147 does not survive export. This is a known issue, and is being investigated.
148 148
149 149 Colors and Highlighting
150 150 =======================
151 151
152 152 Terminal IPython has always had some coloring, but never syntax
153 153 highlighting. There are a few simple color choices, specified by the ``colors``
154 154 flag or ``%colors`` magic:
155 155
156 156 * LightBG for light backgrounds
157 157 * Linux for dark backgrounds
158 158 * NoColor for a simple colorless terminal
159 159
160 160 The Qt widget has full support for the ``colors`` flag used in the terminal shell.
161 161
162 162 The Qt widget, however, has full syntax highlighting as you type, handled by
163 163 the `pygments`_ library. The ``style`` argument exposes access to any style by
164 164 name that can be found by pygments, and there are several already
165 165 installed. The ``colors`` argument, if unspecified, will be guessed based on
166 166 the chosen style. Similarly, there are default styles associated with each
167 167 ``colors`` option.
168 168
169 169
170 170 Screenshot of ``ipython qtconsole --colors=linux``, which uses the 'monokai'
171 171 theme by default:
172 172
173 173 .. image:: figs/colors_dark.png
174 174 :width: 627px
175 175
176 176 .. Note::
177 177
178 178 Calling ``ipython qtconsole -h`` will show all the style names that
179 179 pygments can find on your system.
180 180
181 181 You can also pass the filename of a custom CSS stylesheet, if you want to do
182 182 your own coloring, via the ``stylesheet`` argument. The default LightBG
183 183 stylesheet:
184 184
185 185 .. sourcecode:: css
186 186
187 187 QPlainTextEdit, QTextEdit { background-color: white;
188 188 color: black ;
189 189 selection-background-color: #ccc}
190 190 .error { color: red; }
191 191 .in-prompt { color: navy; }
192 192 .in-prompt-number { font-weight: bold; }
193 193 .out-prompt { color: darkred; }
194 194 .out-prompt-number { font-weight: bold; }
195 195
196 196 Fonts
197 197 =====
198 198
199 199 The QtConsole has configurable via the ConsoleWidget. To change these, set the
200 200 ``font_family`` or ``font_size`` traits of the ConsoleWidget. For instance, to
201 201 use 9pt Anonymous Pro::
202 202
203 203 $> ipython qtconsole --ConsoleWidget.font_family="Anonymous Pro" --ConsoleWidget.font_size=9
204 204
205 205 Process Management
206 206 ==================
207 207
208 208 With the two-process ZMQ model, the frontend does not block input during
209 209 execution. This means that actions can be taken by the frontend while the
210 210 Kernel is executing, or even after it crashes. The most basic such command is
211 211 via 'Ctrl-.', which restarts the kernel. This can be done in the middle of a
212 212 blocking execution. The frontend can also know, via a heartbeat mechanism, that
213 213 the kernel has died. This means that the frontend can safely restart the
214 214 kernel.
215 215
216 216 .. _multiple_consoles:
217 217
218 218 Multiple Consoles
219 219 *****************
220 220
221 221 Since the Kernel listens on the network, multiple frontends can connect to it.
222 222 These do not have to all be qt frontends - any IPython frontend can connect and
223 223 run code. When you start ipython qtconsole, there will be an output line,
224 224 like::
225 225
226 226 [IPKernelApp] To connect another client to this kernel, use:
227 227 [IPKernelApp] --existing --shell=60690 --iopub=44045 --stdin=38323 --hb=41797
228 228
229 229 Other frontends can connect to your kernel, and share in the execution. This is
230 230 great for collaboration. The ``--existing`` flag means connect to a kernel
231 231 that already exists. Starting other
232 232 consoles with that flag will not try to start their own, but rather connect to
233 233 yours. Ultimately, you will not have to specify each port individually, but for
234 234 now this copy-paste method is best.
235 235
236 236 You can even launch a standalone kernel, and connect and disconnect Qt Consoles
237 237 from various machines. This lets you keep the same running IPython session
238 238 on your work machine (with matplotlib plots and everything), logging in from home,
239 239 cafΓ©s, etc.::
240 240
241 241 $> ipython kernel
242 242 [IPKernelApp] To connect another client to this kernel, use:
243 243 [IPKernelApp] --existing --shell=60690 --iopub=44045 --stdin=38323 --hb=41797
244 244
245 245 This is actually exactly the same as the subprocess launched by the qtconsole, so
246 246 all the information about connecting to a standalone kernel is identical to that
247 247 of connecting to the kernel attached to a running console.
248 248
249 249 .. _kernel_security:
250 250
251 251 Security
252 252 --------
253 253
254 254 .. warning::
255 255
256 256 Since the ZMQ code currently has no security, listening on an
257 257 external-facing IP is dangerous. You are giving any computer that can see
258 258 you on the network the ability to issue arbitrary shell commands as you on
259 259 your machine. Read the rest of this section before listening on external ports
260 260 or running an IPython kernel on a shared machine.
261 261
262 262 By default (for security reasons), the kernel only listens on localhost, so you
263 263 can only connect multiple frontends to the kernel from your local machine. You
264 264 can specify to listen on an external interface by specifying the ``ip``
265 265 argument::
266 266
267 267 $> ipython qtconsole --ip=192.168.1.123
268 268
269 269 If you specify the ip as 0.0.0.0, that refers to all interfaces, so any
270 270 computer that can see yours on the network can connect to the kernel.
271 271
272 272 Messages are not encrypted, so users with access to the ports your kernel is using will be
273 273 able to see any output of the kernel. They will also be able to issue shell commands as
274 274 you, unless you enable HMAC digests, which are **DISABLED** by default.
275 275
276 276 The one security feature IPython does provide is protection from unauthorized
277 277 execution. IPython's messaging system can sign messages with HMAC digests using
278 278 a shared-key. The key is never sent over the network, it is only used to generate
279 279 a unique hash for each message, based on its content. When IPython receives a
280 280 message, it will check that the digest matches. You can use any file that only you
281 281 have access to to generate this key. One logical choice would be to use your own
282 282 SSH private key. Or you can generate a new random private key with::
283 283
284 284 # generate 1024b of random data, and store in a file only you can read:
285 285 # (assumes IPYTHON_DIR is defined, otherwise use your IPython directory)
286 286 $> python -c "import os; print os.urandom(128).encode('base64')" > $IPYTHON_DIR/sessionkey
287 287 $> chmod 600 $IPYTHON_DIR/sessionkey
288 288
289 289 To enable HMAC digests, simply specify the ``Session.keyfile`` configurable
290 290 in :file:`ipython_config.py` or at the command-line, as in::
291 291
292 292 # instruct IPython to sign messages with that key:
293 293 $> ipython qtconsole --Session.keyfile=$IPYTHON_DIR/sessionkey
294 294
295 295 You must use the same key you used to start the kernel with all frontends, or
296 296 they will be treated as an unauthorized peer (all messages will be ignored).
297 297
298 298 .. note::
299 299
300 300 IPython will move to using files to store connection information, as is
301 301 done in :mod:`IPython.parallel`, at which point HMAC signatures will be
302 302 enabled *by default*.
303 303
304 304 .. _ssh_tunnels:
305 305
306 306 SSH Tunnels
307 307 -----------
308 308
309 309 Sometimes you want to connect to machines across the internet, or just across
310 310 a LAN that either doesn't permit open ports or you don't trust the other
311 311 machines on the network. To do this, you can use SSH tunnels. SSH tunnels
312 312 are a way to securely forward ports on your local machine to ports on another
313 313 machine, to which you have SSH access.
314 314
315 315 In simple cases, IPython's tools can forward ports over ssh by simply adding the
316 316 ``--ssh=remote`` argument to the usual ``--existing...`` set of flags for connecting
317 317 to a running kernel.
318 318
319 319 .. warning::
320 320
321 321 Using SSH tunnels does *not* increase localhost security. In fact, when
322 322 tunneling from one machine to another *both* machines have open
323 323 ports on localhost available for connections.
324 324
325 325 There are two primary models for using SSH tunnels with IPython. The first
326 326 is to have the Kernel listen only on localhost, and connect to it from
327 327 another machine on the same LAN.
328 328
329 329 First, let's start a kernel on machine **worker**, listening only
330 330 on loopback::
331 331
332 332 user@worker $> ipython kernel
333 333 [IPKernelApp] To connect another client to this kernel, use:
334 334 [IPKernelApp] --existing --shell=59480 --iopub=62199 --stdin=64898 --hb=56511
335 335
336 336 In this case, the IP that you would connect
337 337 to would still be 127.0.0.1, but you want to specify the additional ``--ssh`` argument
338 338 with the hostname of the kernel (in this example, it's 'worker')::
339 339
340 340 user@client $> ipython qtconsole --ssh=worker --existing --shell=59480 --iopub=62199 --stdin=64898 --hb=56511
341 341
342 342 Note again that this opens ports on the *client* machine that point to your kernel.
343 343 Be sure to use a Session key, as described above, if localhost on *either* the
344 344 client or kernel machines is untrusted.
345 345
346 346 .. note::
347 347
348 348 the ssh argument is simply passed to openssh, so it can be fully specified ``user@host:port``
349 349 but it will also respect your aliases, etc. in :file:`.ssh/config` if you have any.
350 350
351 351 The second pattern is for connecting to a machine behind a firewall across the internet
352 352 (or otherwise wide network). This time, we have a machine **login** that you have ssh access
353 353 to, which can see **kernel**, but **client** is on another network. The important difference
354 354 now is that **client** can see **login**, but *not* **worker**. So we need to forward ports from
355 355 client to worker *via* login. This means that the kernel must be started listening
356 356 on external interfaces, so that its ports are visible to `login`::
357 357
358 358 user@worker $> ipython kernel --ip=0.0.0.0
359 359 [IPKernelApp] To connect another client to this kernel, use:
360 360 [IPKernelApp] --existing --shell=59480 --iopub=62199 --stdin=64898 --hb=56511
361 361
362 362 Which we can connect to from the client with::
363 363
364 364 user@client $> ipython qtconsole --ssh=login --ip=192.168.1.123 --existing --shell=59480 --iopub=62199 --stdin=64898 --hb=56511
365 365
366 366 Note that now the IP is the address of worker as seen from login.
367 367
368 368 Manual SSH tunnels
369 369 ------------------
370 370
371 371 It's possible that IPython's ssh helper functions won't work for you, for various
372 372 reasons. You can still connect to remote machines, as long as you set up the tunnels
373 373 yourself. The basic format of forwarding a local port to a remote one is::
374 374
375 375 [client] $> ssh <server> <localport>:<remoteip>:<remoteport> -f -N
376 376
377 377 This will forward local connections to **localport** on client to **remoteip:remoteport**
378 378 *via* **server**. Note that remoteip is interpreted relative to *server*, not the client.
379 379 So if you have direct ssh access to the machine to which you want to forward connections,
380 380 then the server *is* the remote machine, and remoteip should be server's IP as seen from the
381 381 server itself, i.e. 127.0.0.1. Thus, to forward local port 12345 to remote port 54321 on
382 382 a machine you can see, do::
383 383
384 384 [client] $> ssh machine 12345:127.0.0.1:54321 -f -N
385 385
386 386 But if your target is actually on a LAN at 192.168.1.123, behind another machine called **login**,
387 387 then you would do::
388 388
389 389 [client] $> ssh login 12345:192.168.1.16:54321 -f -N
390 390
391 391 The ``-f -N`` on the end are flags that tell ssh to run in the background,
392 392 and don't actually run any commands beyond creating the tunnel.
393 393
394 394 .. seealso::
395 395
396 396 A short discussion of ssh tunnels: http://www.revsys.com/writings/quicktips/ssh-tunnel.html
397 397
398 398
399 399
400 400 Stopping Kernels and Consoles
401 401 *****************************
402 402
403 403 Since there can be many consoles per kernel, the shutdown mechanism and dialog
404 404 are probably more complicated than you are used to. Since you don't always want
405 405 to shutdown a kernel when you close a window, you are given the option to just
406 406 close the console window or also close the Kernel and *all other windows*. Note
407 407 that this only refers to all other *local* windows, as remote Consoles are not
408 408 allowed to shutdown the kernel, and shutdowns do not close Remote consoles (to
409 409 allow for saving, etc.).
410 410
411 411 Rules:
412 412
413 413 * Restarting the kernel automatically clears all *local* Consoles, and prompts remote
414 414 Consoles about the reset.
415 415 * Shutdown closes all *local* Consoles, and notifies remotes that
416 416 the Kernel has been shutdown.
417 417 * Remote Consoles may not restart or shutdown the kernel.
418 418
419 419 Qt and the QtConsole
420 420 ====================
421 421
422 422 An important part of working with the QtConsole when you are writing your own
423 423 Qt code is to remember that user code (in the kernel) is *not* in the same
424 424 process as the frontend. This means that there is not necessarily any Qt code
425 425 running in the kernel, and under most normal circumstances there isn't. If,
426 426 however, you specify ``--pylab=qt`` at the command-line, then there *will* be a
427 427 :class:`QCoreApplication` instance running in the kernel process along with
428 428 user-code. To get a reference to this application, do:
429 429
430 430 .. sourcecode:: python
431 431
432 432 from PyQt4 import QtCore
433 433 app = QtCore.QCoreApplication.instance()
434 434 # app will be None if there is no such instance
435 435
436 436 A common problem listed in the PyQt4 Gotchas_ is the fact that Python's garbage
437 437 collection will destroy Qt objects (Windows, etc.) once there is no longer a
438 438 Python reference to them, so you have to hold on to them. For instance, in:
439 439
440 440 .. sourcecode:: python
441 441
442 442 def make_window():
443 443 win = QtGui.QMainWindow()
444 444
445 445 def make_and_return_window():
446 446 win = QtGui.QMainWindow()
447 447 return win
448 448
449 449 :func:`make_window` will never draw a window, because garbage collection will
450 450 destroy it before it is drawn, whereas :func:`make_and_return_window` lets the
451 451 caller decide when the window object should be destroyed. If, as a developer,
452 452 you know that you always want your objects to last as long as the process, you
453 453 can attach them to the QApplication instance itself:
454 454
455 455 .. sourcecode:: python
456 456
457 457 # do this just once:
458 458 app = QtCore.QCoreApplication.instance()
459 459 app.references = set()
460 460 # then when you create Windows, add them to the set
461 461 def make_window():
462 462 win = QtGui.QMainWindow()
463 463 app.references.add(win)
464 464
465 465 Now the QApplication itself holds a reference to ``win``, so it will never be
466 466 garbage collected until the application itself is destroyed.
467 467
468 468 .. _Gotchas: http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/gotchas.html#garbage-collection
469 469
470 470 Regressions
471 471 ===========
472 472
473 473 There are some features, where the qt console lags behind the Terminal
474 474 frontend:
475 475
476 476 * !cmd input: Due to our use of pexpect, we cannot pass input to subprocesses
477 477 launched using the '!' escape, so you should never call a command that
478 478 requires interactive input. For such cases, use the terminal IPython. This
479 479 will not be fixed, as abandoning pexpect would significantly degrade the
480 480 console experience.
481 481
482 482 * Use of ``\b`` and ``\r`` characters in the console: these are control
483 483 characters that allow the cursor to move backwards on a line, and are used to
484 484 display things like in-place progress bars in a terminal. We currently do
485 485 not support this, but it is being tracked as issue 629_.
486 486
487 487 .. _629: https://github.com/ipython/ipython/issues/629
488 488
489 489 .. [PyQt] PyQt4 http://www.riverbankcomputing.co.uk/software/pyqt/download
490 490 .. [pygments] Pygments http://pygments.org/
General Comments 0
You need to be logged in to leave comments. Login now