Show More
@@ -8,7 +8,7 b' are at the bottom of the file, the rest is left untouched.' | |||||
8 | Must be loaded with ip.load('ipy_bzr') |
|
8 | Must be loaded with ip.load('ipy_bzr') | |
9 |
|
9 | |||
10 | """ |
|
10 | """ | |
11 | a |
|
11 | ||
12 | # Copyright (C) 2004, 2005 Aaron Bentley |
|
12 | # Copyright (C) 2004, 2005 Aaron Bentley | |
13 | # <aaron@aaronbentley.com> |
|
13 | # <aaron@aaronbentley.com> | |
14 | # |
|
14 | # |
@@ -358,18 +358,13 b' class MTInteractiveShell(InteractiveShell):' | |||||
358 | InteractiveShell.__init__(self,name,usage,rc,user_ns, |
|
358 | InteractiveShell.__init__(self,name,usage,rc,user_ns, | |
359 | user_global_ns,banner2) |
|
359 | user_global_ns,banner2) | |
360 |
|
360 | |||
361 | # Locking control variable. |
|
|||
362 | self.thread_ready = threading.Condition(threading.RLock()) |
|
|||
363 |
|
361 | |||
364 |
# A queue to hold the code to be executed. |
|
362 | # A queue to hold the code to be executed. | |
365 | # enough, because uses like macros cause reentrancy. |
|
|||
366 | self.code_queue = Queue.Queue() |
|
363 | self.code_queue = Queue.Queue() | |
367 |
|
364 | |||
368 | # Stuff to do at closing time |
|
365 | # Stuff to do at closing time | |
369 |
self._kill = |
|
366 | self._kill = None | |
370 | on_kill = kw.get('on_kill') |
|
367 | on_kill = kw.get('on_kill', []) | |
371 | if on_kill is None: |
|
|||
372 | on_kill = [] |
|
|||
373 | # Check that all things to kill are callable: |
|
368 | # Check that all things to kill are callable: | |
374 | for t in on_kill: |
|
369 | for t in on_kill: | |
375 | if not callable(t): |
|
370 | if not callable(t): | |
@@ -377,18 +372,23 b' class MTInteractiveShell(InteractiveShell):' | |||||
377 | self.on_kill = on_kill |
|
372 | self.on_kill = on_kill | |
378 | # thread identity of the "worker thread" (that may execute code directly) |
|
373 | # thread identity of the "worker thread" (that may execute code directly) | |
379 | self.worker_ident = None |
|
374 | self.worker_ident = None | |
|
375 | ||||
380 | def runsource(self, source, filename="<input>", symbol="single"): |
|
376 | def runsource(self, source, filename="<input>", symbol="single"): | |
381 | """Compile and run some source in the interpreter. |
|
377 | """Compile and run some source in the interpreter. | |
382 |
|
378 | |||
383 | Modified version of code.py's runsource(), to handle threading issues. |
|
379 | Modified version of code.py's runsource(), to handle threading issues. | |
384 | See the original for full docstring details.""" |
|
380 | See the original for full docstring details.""" | |
385 |
|
381 | |||
386 | global KBINT |
|
382 | global KBINT | |
387 |
|
383 | |||
388 | # If Ctrl-C was typed, we reset the flag and return right away |
|
384 | # If Ctrl-C was typed, we reset the flag and return right away | |
389 | if KBINT: |
|
385 | if KBINT: | |
390 | KBINT = False |
|
386 | KBINT = False | |
391 | return False |
|
387 | return False | |
|
388 | ||||
|
389 | if self._kill: | |||
|
390 | # can't queue new code if we are being killed | |||
|
391 | return True | |||
392 |
|
392 | |||
393 | try: |
|
393 | try: | |
394 | code = self.compile(source, filename, symbol) |
|
394 | code = self.compile(source, filename, symbol) | |
@@ -401,38 +401,40 b' class MTInteractiveShell(InteractiveShell):' | |||||
401 | # Case 2 |
|
401 | # Case 2 | |
402 | return True |
|
402 | return True | |
403 |
|
403 | |||
404 | # Case 3 |
|
|||
405 | # Store code in queue, so the execution thread can handle it. |
|
|||
406 |
|
||||
407 | # Note that with macros and other applications, we MAY re-enter this |
|
|||
408 | # section, so we have to acquire the lock with non-blocking semantics, |
|
|||
409 | # else we deadlock. |
|
|||
410 |
|
||||
411 | # shortcut - if we are in worker thread, or the worker thread is not running, |
|
404 | # shortcut - if we are in worker thread, or the worker thread is not running, | |
412 | # execute directly (to allow recursion and prevent deadlock if code is run early |
|
405 | # execute directly (to allow recursion and prevent deadlock if code is run early | |
413 | # in IPython construction) |
|
406 | # in IPython construction) | |
414 |
|
407 | |||
415 | if self.worker_ident is None or self.worker_ident == thread.get_ident(): |
|
408 | if (self.worker_ident is None or self.worker_ident == thread.get_ident()): | |
416 | InteractiveShell.runcode(self,code) |
|
409 | InteractiveShell.runcode(self,code) | |
417 | return |
|
410 | return | |
418 |
|
||||
419 | got_lock = self.thread_ready.acquire(blocking=False) |
|
|||
420 | self.code_queue.put(code) |
|
|||
421 | if got_lock: |
|
|||
422 | self.thread_ready.wait() # Wait until processed in timeout interval |
|
|||
423 | self.thread_ready.release() |
|
|||
424 |
|
411 | |||
|
412 | # Case 3 | |||
|
413 | # Store code in queue, so the execution thread can handle it. | |||
|
414 | ||||
|
415 | completed_ev, received_ev = threading.Event(), threading.Event() | |||
|
416 | ||||
|
417 | self.code_queue.put((code,completed_ev, received_ev)) | |||
|
418 | # first make sure the message was received, with timeout | |||
|
419 | received_ev.wait(5) | |||
|
420 | if not received_ev.isSet(): | |||
|
421 | # the mainloop is dead, start executing code directly | |||
|
422 | print "Warning: Timeout for mainloop thread exceeded" | |||
|
423 | print "switching to nonthreaded mode (until mainloop wakes up again)" | |||
|
424 | self.worker_ident = None | |||
|
425 | else: | |||
|
426 | completed_ev.wait() | |||
425 | return False |
|
427 | return False | |
426 |
|
428 | |||
427 | def runcode(self): |
|
429 | def runcode(self): | |
428 | """Execute a code object. |
|
430 | """Execute a code object. | |
429 |
|
431 | |||
430 | Multithreaded wrapper around IPython's runcode().""" |
|
432 | Multithreaded wrapper around IPython's runcode().""" | |
431 |
|
433 | |||
432 | global CODE_RUN |
|
434 | global CODE_RUN | |
433 | # lock thread-protected stuff |
|
435 | ||
|
436 | # we are in worker thread, stash out the id for runsource() | |||
434 | self.worker_ident = thread.get_ident() |
|
437 | self.worker_ident = thread.get_ident() | |
435 | got_lock = self.thread_ready.acquire() |
|
|||
436 |
|
438 | |||
437 | if self._kill: |
|
439 | if self._kill: | |
438 | print >>Term.cout, 'Closing threads...', |
|
440 | print >>Term.cout, 'Closing threads...', | |
@@ -440,6 +442,9 b' class MTInteractiveShell(InteractiveShell):' | |||||
440 | for tokill in self.on_kill: |
|
442 | for tokill in self.on_kill: | |
441 | tokill() |
|
443 | tokill() | |
442 | print >>Term.cout, 'Done.' |
|
444 | print >>Term.cout, 'Done.' | |
|
445 | # allow kill() to return | |||
|
446 | self._kill.set() | |||
|
447 | return True | |||
443 |
|
448 | |||
444 | # Install sigint handler. We do it every time to ensure that if user |
|
449 | # Install sigint handler. We do it every time to ensure that if user | |
445 | # code modifies it, we restore our own handling. |
|
450 | # code modifies it, we restore our own handling. | |
@@ -455,9 +460,11 b' class MTInteractiveShell(InteractiveShell):' | |||||
455 | code_to_run = None |
|
460 | code_to_run = None | |
456 | while 1: |
|
461 | while 1: | |
457 | try: |
|
462 | try: | |
458 | code_to_run = self.code_queue.get_nowait() |
|
463 | code_to_run, completed_ev, received_ev = self.code_queue.get_nowait() | |
459 | except Queue.Empty: |
|
464 | except Queue.Empty: | |
460 | break |
|
465 | break | |
|
466 | received_ev.set() | |||
|
467 | ||||
461 | # Exceptions need to be raised differently depending on which |
|
468 | # Exceptions need to be raised differently depending on which | |
462 | # thread is active. This convoluted try/except is only there to |
|
469 | # thread is active. This convoluted try/except is only there to | |
463 | # protect against asynchronous exceptions, to ensure that a KBINT |
|
470 | # protect against asynchronous exceptions, to ensure that a KBINT | |
@@ -471,28 +478,23 b' class MTInteractiveShell(InteractiveShell):' | |||||
471 | except KeyboardInterrupt: |
|
478 | except KeyboardInterrupt: | |
472 | print "Keyboard interrupted in mainloop" |
|
479 | print "Keyboard interrupted in mainloop" | |
473 | while not self.code_queue.empty(): |
|
480 | while not self.code_queue.empty(): | |
474 | self.code_queue.get_nowait() |
|
481 | code, ev1,ev2 = self.code_queue.get_nowait() | |
|
482 | ev1.set() | |||
|
483 | ev2.set() | |||
475 | break |
|
484 | break | |
476 | finally: |
|
485 | finally: | |
477 |
|
|
486 | CODE_RUN = False | |
478 | CODE_RUN = False |
|
487 | # allow runsource() return from wait | |
479 |
|
488 | completed_ev.set() | ||
480 | # We're done with thread-protected variables |
|
489 | ||
481 | if code_to_run is not None: |
|
490 | ||
482 | self.thread_ready.notify() |
|
|||
483 | self.thread_ready.release() |
|
|||
484 |
|
||||
485 | # We're done... |
|
|||
486 | CODE_RUN = False |
|
|||
487 | # This MUST return true for gtk threading to work |
|
491 | # This MUST return true for gtk threading to work | |
488 | return True |
|
492 | return True | |
489 |
|
493 | |||
490 | def kill(self): |
|
494 | def kill(self): | |
491 | """Kill the thread, returning when it has been shut down.""" |
|
495 | """Kill the thread, returning when it has been shut down.""" | |
492 | got_lock = self.thread_ready.acquire(False) |
|
496 | self._kill = threading.Event() | |
493 |
self._kill |
|
497 | self._kill.wait() | |
494 | if got_lock: |
|
|||
495 | self.thread_ready.release() |
|
|||
496 |
|
498 | |||
497 | class MatplotlibShellBase: |
|
499 | class MatplotlibShellBase: | |
498 | """Mixin class to provide the necessary modifications to regular IPython |
|
500 | """Mixin class to provide the necessary modifications to regular IPython | |
@@ -1153,10 +1155,13 b' def _select_shell(argv):' | |||||
1153 | } |
|
1155 | } | |
1154 |
|
1156 | |||
1155 | all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread', |
|
1157 | all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread', | |
1156 | 'tkthread']) |
|
1158 | 'tkthread', 'twisted']) | |
1157 | user_opts = set([s.replace('-','') for s in argv[:3]]) |
|
1159 | user_opts = set([s.replace('-','') for s in argv[:3]]) | |
1158 | special_opts = user_opts & all_opts |
|
1160 | special_opts = user_opts & all_opts | |
1159 |
|
1161 | |||
|
1162 | if 'twisted' in special_opts: | |||
|
1163 | import twshell | |||
|
1164 | return twshell.IPShellTwisted | |||
1160 | if 'tk' in special_opts: |
|
1165 | if 'tk' in special_opts: | |
1161 | USE_TK = True |
|
1166 | USE_TK = True | |
1162 | special_opts.remove('tk') |
|
1167 | special_opts.remove('tk') |
@@ -184,10 +184,9 b" object? -> Details about 'object'. ?object also works, ?? prints more." | |||||
184 |
|
184 | |||
185 | # Options that can *only* appear at the cmd line (not in rcfiles). |
|
185 | # Options that can *only* appear at the cmd line (not in rcfiles). | |
186 |
|
186 | |||
187 | # The "ignore" option is a kludge so that Emacs buffers don't crash, since |
|
|||
188 | # the 'C-c !' command in emacs automatically appends a -i option at the end. |
|
|||
189 | cmdline_only = ('help interact|i ipythondir=s Version upgrade ' |
|
187 | cmdline_only = ('help interact|i ipythondir=s Version upgrade ' | |
190 |
'gthread! qthread! q4thread! wthread! tkthread! pylab! tk!' |
|
188 | 'gthread! qthread! q4thread! wthread! tkthread! pylab! tk! ' | |
|
189 | 'twisted!') | |||
191 |
|
190 | |||
192 | # Build the actual name list to be used by DPyGetOpt |
|
191 | # Build the actual name list to be used by DPyGetOpt | |
193 | opts_names = qw(cmdline_opts) + qw(cmdline_only) |
|
192 | opts_names = qw(cmdline_opts) + qw(cmdline_only) | |
@@ -245,6 +244,7 b" object? -> Details about 'object'. ?object also works, ?? prints more." | |||||
245 | system_verbose = 0, |
|
244 | system_verbose = 0, | |
246 | term_title = 1, |
|
245 | term_title = 1, | |
247 | tk = 0, |
|
246 | tk = 0, | |
|
247 | twisted= 0, | |||
248 | upgrade = 0, |
|
248 | upgrade = 0, | |
249 | Version = 0, |
|
249 | Version = 0, | |
250 | wildcards_case_sensitive = 1, |
|
250 | wildcards_case_sensitive = 1, |
@@ -80,3 +80,17 b' else:' | |||||
80 | Term.cout.flush() |
|
80 | Term.cout.flush() | |
81 | # Set global flag so that runsource can know that Ctrl-C was hit |
|
81 | # Set global flag so that runsource can know that Ctrl-C was hit | |
82 | KBINT = True |
|
82 | KBINT = True | |
|
83 | ||||
|
84 | def run_in_frontend(src): | |||
|
85 | """ Check if source snippet can be run in the REPL thread, as opposed to GUI mainloop | |||
|
86 | ||||
|
87 | (to prevent unnecessary hanging of mainloop). | |||
|
88 | ||||
|
89 | """ | |||
|
90 | ||||
|
91 | if src.startswith('_ip.system(') and not '\n' in src: | |||
|
92 | return True | |||
|
93 | return False | |||
|
94 | ||||
|
95 | ||||
|
96 |
@@ -1,13 +1,7 b'' | |||||
1 | import sys |
|
1 | import sys | |
2 |
|
2 | |||
3 | from twisted.internet import gtk2reactor |
|
|||
4 | gtk2reactor.install() |
|
|||
5 |
|
||||
6 | from twisted.internet import reactor, threads |
|
3 | from twisted.internet import reactor, threads | |
7 |
|
4 | |||
8 | """ change here to choose the plot shell with the MT option |
|
|||
9 | which should cost extra """ |
|
|||
10 |
|
||||
11 | from IPython.ipmaker import make_IPython |
|
5 | from IPython.ipmaker import make_IPython | |
12 | from IPython.iplib import InteractiveShell |
|
6 | from IPython.iplib import InteractiveShell | |
13 | from IPython.ipstruct import Struct |
|
7 | from IPython.ipstruct import Struct | |
@@ -16,6 +10,11 b' from signal import signal, SIGINT' | |||||
16 | from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no |
|
10 | from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no | |
17 | import shellglobals |
|
11 | import shellglobals | |
18 |
|
12 | |||
|
13 | def install_gtk2(): | |||
|
14 | """ Install gtk2 reactor, needs to be called bef """ | |||
|
15 | from twisted.internet import gtk2reactor | |||
|
16 | gtk2reactor.install() | |||
|
17 | ||||
19 |
|
18 | |||
20 | def hijack_reactor(): |
|
19 | def hijack_reactor(): | |
21 | """Modifies Twisted's reactor with a dummy so user code does |
|
20 | """Modifies Twisted's reactor with a dummy so user code does | |
@@ -106,7 +105,7 b' class TwistedInteractiveShell(InteractiveShell):' | |||||
106 | # in IPython construction) |
|
105 | # in IPython construction) | |
107 |
|
106 | |||
108 | if (not self.reactor_started or (self.worker_ident is None and not self.first_run) |
|
107 | if (not self.reactor_started or (self.worker_ident is None and not self.first_run) | |
109 | or self.worker_ident == thread.get_ident()): |
|
108 | or self.worker_ident == thread.get_ident() or shellglobals.run_in_frontend(source)): | |
110 | InteractiveShell.runcode(self,code) |
|
109 | InteractiveShell.runcode(self,code) | |
111 | return |
|
110 | return | |
112 |
|
111 | |||
@@ -125,8 +124,7 b' class TwistedInteractiveShell(InteractiveShell):' | |||||
125 | print "Warning: Timeout for mainloop thread exceeded" |
|
124 | print "Warning: Timeout for mainloop thread exceeded" | |
126 | print "switching to nonthreaded mode (until mainloop wakes up again)" |
|
125 | print "switching to nonthreaded mode (until mainloop wakes up again)" | |
127 | self.worker_ident = None |
|
126 | self.worker_ident = None | |
128 | else: |
|
127 | else: | |
129 | shellglobals.CURRENT_COMPLETE_EV = completed_ev |
|
|||
130 | completed_ev.wait() |
|
128 | completed_ev.wait() | |
131 |
|
129 | |||
132 | return False |
|
130 | return False |
@@ -36,11 +36,11 b' The following special options are ONLY valid at the beginning of the command' | |||||
36 | line, and not later. This is because they control the initialization of |
|
36 | line, and not later. This is because they control the initialization of | |
37 | ipython itself, before the normal option-handling mechanism is active. |
|
37 | ipython itself, before the normal option-handling mechanism is active. | |
38 | .TP |
|
38 | .TP | |
39 | .B \-gthread, \-qthread, \-q4thread, \-wthread, \-pylab |
|
39 | .B \-gthread, \-qthread, \-q4thread, \-wthread, \-pylab, \-twisted | |
40 | Only ONE of these can be given, and it can only be given as the first option |
|
40 | Only ONE of these can be given, and it can only be given as the first option | |
41 | passed to IPython (it will have no effect in any other position). They provide |
|
41 | passed to IPython (it will have no effect in any other position). They provide | |
42 |
threading support for the GTK, QT3, QT4 and WXWidgets toolkits, |
|
42 | threading support for the GTK, QT3, QT4 and WXWidgets toolkits, for the | |
43 | matplotlib library. |
|
43 | matplotlib library and Twisted reactor. | |
44 | .br |
|
44 | .br | |
45 | .sp 1 |
|
45 | .sp 1 | |
46 | With any of the first four options, IPython starts running a separate thread |
|
46 | With any of the first four options, IPython starts running a separate thread | |
@@ -56,6 +56,10 b' request a specific version of wx to be used. This requires that you have the' | |||||
56 | distributions. |
|
56 | distributions. | |
57 | .br |
|
57 | .br | |
58 | .sp 1 |
|
58 | .sp 1 | |
|
59 | If \-twisted is given, IPython start a Twisted reactor and runs IPython mainloop | |||
|
60 | in a dedicated thread, passing commands to be run inside the Twisted reactor. | |||
|
61 | .br | |||
|
62 | .sp 1 | |||
59 | If \-pylab is given, IPython loads special support for the matplotlib library |
|
63 | If \-pylab is given, IPython loads special support for the matplotlib library | |
60 | (http://matplotlib.sourceforge.net), allowing interactive usage of any of its |
|
64 | (http://matplotlib.sourceforge.net), allowing interactive usage of any of its | |
61 | backends as defined in the user's .matplotlibrc file. It automatically |
|
65 | backends as defined in the user's .matplotlibrc file. It automatically |
General Comments 0
You need to be logged in to leave comments.
Login now