Merge lp://qastaging/~ipython-dev/ipython/inputhook into lp://qastaging/ipython/0.11
- inputhook
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp://qastaging/~ipython-dev/ipython/inputhook |
Merge into: | lp://qastaging/ipython/0.11 |
Diff against target: | None lines |
To merge this branch: | bzr merge lp://qastaging/~ipython-dev/ipython/inputhook |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Fernando Perez | Needs Information | ||
Review via email:
|
Commit message
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brian Granger (ellisonbg) wrote : | # |
- 1231. By Brian Granger
-
Work on the user focused GUI event loop interface.
* Old command line options (pylab/
wthread/ qthread) are deprecated.
* New %gui magic to manage the events loops.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brian Granger (ellisonbg) wrote : | # |
I have done more work to clean up the user interface. Here is the latest changes:
New features
------------
* Added a new module :mod:`IPython.
with GUI event loops using `PyOS_InputHook`. See the docstrings in this
module for details.
* For users, GUI event loop integration is now handled through the new
:command:`%gui` magic command. Type ``%gui?`` at an IPython prompt for
documentation.
To use matplotlib with the new GUI event loop integration do the following
in IPython::
%gui -a wx
import matplotlib
matplotlib.
from matplotlib import pyplot
pyplot.
# Then do your plotting
Bug fixes
---------
* Keyboard interrupts now work with GUI support enabled across all platforms
and all GUI toolkits reliably.
Backwards incompatible changes
-------
* Removed :mod:`shellglobals` as it was obsolete.
* Removed all the threaded shells in :mod:`IPython.
longer needed because of the new capabilities in
:mod:
* The old threading command line flags (pylab/
deprecated. Use :mod:`IPython.
command instead.
- 1232. By Brian Granger
-
Merging upstream changes.
- 1233. By Brian Granger
-
General work on inputhook and the docs.
* Re-wrote the Sphinx docs on GUI integration.
* Improved docstrings of inputhook module.
* Added current_gui function to inputhook.
* Fixed lots of small bugs in the docs.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
I'm fried so the notes below are less complete than they should... But I want them in now, in case you see this tomorrow early before I'm up, we can talk about it further later.
Notes on the review of the inputhook branch...
- shouldn't -a be the default for %gui?
- why %gui tk? I thought it wasn't really needed since Python already does
this...
- why %gui with no args? What does it mean?
- Small tests with standalone wx/gtk/qt codes besides matplotlib? It would be
good to have little snippets with each toolkit in our test suite, even if we
need to run those by hand each time. At least knowing where to find them
would help.
- Is there no support for qt3?
- %gui gtk appears to be broken:
/home/fperez/
177 # For older versions of gtk, use our own ctypes version
178 from IPython.
--> 179 add_inputhook(
180
181 def disable_gtk(self):
NameError: global name 'add_inputhook' is not defined
- My little test script crashes with wx, if I hit Ctrl-C before it's finished.
Here is the code, just put it in a file (I called it 'switchgui') and run it
as indicated in the docstring.
"""Test the new %gui command. Run this in ipython as
%run switchgui [backend]
where the optional backend can be one of: qt4, gtk, tk, wx.
"""
import sys
import time
import matplotlib
try:
import IPython.core.ipapi as ipapi
except ImportError:
import IPython.ipapi as ipapi
ip = ipapi.get()
else:
ip = ipapi.get()
try:
guitk = sys.argv[1]
except IndexError:
guitk = ''
backends = dict(wx='wxagg', qt4='qt4agg', gtk='gtkagg', tk='tkagg')
backends[''] = 'tkagg'
matplotlib.
matplotlib.
from matplotlib import pyplot as plt, mlab, pylab
import numpy as np
from numpy import *
from matplotlib.pyplot import *
x = np.arange(10)
print "A plot has been created"
plot(x)
plt.show()
print "I will now count until 10, please hit Ctrl-C before I'm done..."
print "IPython should stop counting and return to the prompt without crashing."
print
for i in range(1,11):
print i,
sys.
time.sleep(1)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brian Granger (ellisonbg) wrote : | # |
> Notes on the review of the inputhook branch...
>
> - shouldn't -a be the default for %gui?
>
Probably.
- why %gui tk? I thought it wasn't really needed since Python already does
>
Just to provide a uniform interface. Also, when %gui is called, I
internally set a variable that points to the current gui, and it can be
retrieved using:
from IPython.
current_gui()
> this...
> - why %gui with no args? What does it mean?
>
It removes all gui event loop support.
> - Small tests with standalone wx/gtk/qt codes besides matplotlib? It would
> be
> good to have little snippets with each toolkit in our test suite, even if
> we
> need to run those by hand each time. At least knowing where to find them
> would help.
>
Yes, these should go into examples.
> - Is there no support for qt3?
>
Nope, it shouldn't be difficult to add, but I am not going to do it right
now.
> - %gui gtk appears to be broken:
>
> /home/fperez/
> enable_gtk(self, app)
> 177 # For older versions of gtk, use our own ctypes version
>
> 178 from IPython.
> --> 179 add_inputhook(
> 180
> 181 def disable_gtk(self):
>
> NameError: global name 'add_inputhook' is not defined
>
Ooops, I just fixed this (haven't pushed yet).
> - My little test script crashes with wx, if I hit Ctrl-C before it's
> finished.
> Here is the code, just put it in a file (I called it 'switchgui') and run
> it
> as indicated in the docstring.
>
There are a couple of issues with this:
* The thing that triggers the event loop to run is something being ready on
stdin. Thus, because your script doesn't have anything ready on stdin, the
event loop doesn't run.
* Another way of saying it is this: gui event loop is in the main thread.
Your code is in the main thread (it has to be because it is using the
gui!). Thus, as long as user code is running, the gui can't run.
We might be able to find a way of getting the event loop to run by piping
something to stdin though. We should look at this.
>
> """Test the new %gui command. Run this in ipython as
>
> %run switchgui [backend]
>
> where the optional backend can be one of: qt4, gtk, tk, wx.
> """
>
> import sys
> import time
>
> import matplotlib
>
> try:
> import IPython.core.ipapi as ipapi
> except ImportError:
> import IPython.ipapi as ipapi
> ip = ipapi.get()
> else:
> ip = ipapi.get()
> try:
> guitk = sys.argv[1]
> ip.magic('gui -a %s' % guitk)
> except IndexError:
> guitk = ''
> backends = dict(wx='wxagg', qt4='qt4agg', gtk='gtkagg', tk='tkagg')
> backends[''] = 'tkagg'
> matplotlib.
> matplotlib.
>
> from matplotlib import pyplot as plt, mlab, pylab
> import numpy as np
>
> from numpy import *
> from matplotlib.pyplot import *
>
> x = np.arange(10)
>
> print "A plot has been created"
> plot(x)
> plt.show()
>
> print "I will now count until 10, please hit Ctrl-C before I'm done..."
> print "IPython should stop counting and return to the prompt without
> ...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ville M. Vainio (villemvainio) wrote : | # |
On Fri, Aug 21, 2009 at 7:45 PM, Brian Granger<email address hidden> wrote:
> * Another way of saying it is this: gui event loop is in the main thread.
> Your code is in the main thread (it has to be because it is using the
> gui!). Thus, as long as user code is running, the gui can't run.
>
> We might be able to find a way of getting the event loop to run by piping
> something to stdin though. We should look at this.
Unlikely, as event loop is run when you are doing raw_input
(regardless of whether you have data there or not). If you are not
entering raw_input part of repl, your event loop is starving. No easy
way around that.
--
Ville M. Vainio
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brian Granger (ellisonbg) wrote : | # |
> Unlikely, as event loop is run when you are doing raw_input
> (regardless of whether you have data there or not). If you are not
> entering raw_input part of repl, your event loop is starving. No easy
> way around that.
>
Yes this is right. Ignore what I said before it was wrong. Here is a
modified version of Fernando's script that does work.
"""Test the new %gui command. Run this in ipython as
%gui -a wx # select the gui here
%run switchgui
"""
import sys
import time
import matplotlib
from IPython.
guitk = current_gui()
if guitk is None:
raise RuntimeError('You must enable gui event loop first using %gui')
backends = dict(wx='wxagg', qt4='qt4agg', gtk='gtkagg', tk='tkagg')
matplotlib.
matplotlib.
from matplotlib import pyplot as plt, mlab, pylab
import numpy as np
from numpy import *
from matplotlib.pyplot import *
x = np.arange(10)
print "A plot has been created"
plot(x)
plt.show()
raw_input("Press Enter to continue")
# The plot won't show up here as the event loop only runs when the
# command prompt is sitting and doing nothing.
print "I will now count until 10, please hit Ctrl-C before I'm done..."
print "IPython should stop counting and return to the prompt without
crashing."
print
for i in range(1,11):
print i,
sys.
time.sleep(1)
--
Brian E. Granger, Ph.D.
Assistant Professor of Physics
Cal Poly State University, San Luis Obispo
<email address hidden>
<email address hidden>
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
On Fri, Aug 21, 2009 at 10:00 AM, Brian Granger<email address hidden> wrote:
>
> raw_input("Press Enter to continue")
Mmh, still segfaults:
In [1]: run switchgui.py wx
A plot has been created
Press Enter to continue
I will now count until 10, please hit Ctrl-C before I'm done...
IPython should stop counting and return to the prompt without crashing.
1 2^C
maqroll[ipython]> # OOPS, why am I here? segfault...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brian Granger (ellisonbg) wrote : | # |
Did you do:
In [1]: %gui -a wx
First?
On Fri, Aug 21, 2009 at 11:33 AM, Fernando Perez <email address hidden>wrote:
> On Fri, Aug 21, 2009 at 10:00 AM, Brian Granger<email address hidden>
> wrote:
> >
> > raw_input("Press Enter to continue")
>
> Mmh, still segfaults:
>
> In [1]: run switchgui.py wx
> A plot has been created
> Press Enter to continue
>
> I will now count until 10, please hit Ctrl-C before I'm done...
> IPython should stop counting and return to the prompt without crashing.
>
> 1 2^C
> maqroll[ipython]> # OOPS, why am I here? segfault...
> --
> https:/
> You proposed lp:~ipython-dev/ipython/inputhook for merging.
>
--
Brian E. Granger, Ph.D.
Assistant Professor of Physics
Cal Poly State University, San Luis Obispo
<email address hidden>
<email address hidden>
- 1234. By Brian Granger
-
Fixed two bugs in inputhook.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
This is for the long-term record: Brian and I have just found out that
the Wx crash was apparently a Wx bug, that we can fortunately work
around ourselves.
So for now this is OK, we'll continue reviewing the code.
- 1235. By Brian Granger
-
Work on inputhook.
* We now hold references to qt4 and wx apps that are created.
* For wx and qt4 we now first try to get an existing app if it exists.
* We now hijack the mainloops for all gui toolkits.
* Longer poll time for wx. - 1236. By Brian Granger
-
Adding more documentation of inputhook.py
- 1237. By Brian Granger
-
Fixed a few bugs and added spin_qt4 and spin_wx.
- 1238. By Fernando Perez
-
Added a Qt4 simple example to test %gui functionality.
This is put in examples/ and not as a test because it requires manual
interaction and opens windows, but this way we can test it at least manually
during development. Next we'll add similar ones for the other toolkits. - 1239. By Fernando Perez
-
Added an improved mainloop manager.
Currently only fully functional for Qt4, we're implementing it for the others.
- 1240. By Fernando Perez
-
In-progress work on trying to get a robust inputhook setup.
This is proving to be pretty hard and will require some changes to end-user
code in the long run. It's simply impossible. - 1241. By Brian Granger
-
Finishing up the wx, qt4 and tk support. Still have to do gtk.
- 1242. By Brian Granger
-
Renaming mpl example.
- 1243. By Brian Granger
-
More work on the Tk and GTK gui integration.
- 1244. By Brian Granger
-
Mostly finished on edits to inputhook.
- 1245. By Brian Granger
-
Minor doc update.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
- Trivial problem in gtk example:
In [2]: run gui-gtk.py
-------
NameError Traceback (most recent call last)
/home/fperez/
22 button.
23
---> 24 window.
25 button.show()
26 window.show()
NameError: name 'self' is not defined
WARNING: Failure executing file: <gui-gtk.py>
I've fixed this and pushed the tiny change so I could keep my working branch synced; go ahead and pull before making further commits.
But I still wonder why (after the fix) this gtk example blocks completely ipython *after closing the gtk window*. That is, if I do *not* type '%gui gtk' first, and then '%run gui-gtk', the gtk window blocks the terminal, as it should. But even if I close the window, I can never seem to get the terminal back.
It may be that the actual destroy event hasn't been wired, but it would be good to have an example that works for our users to use as reference (that is, one which only blocks while the window is open if they forget the %gui call, but that doesn't leave ipython locked forever).
If I then try to Ctrl-C after closing the gtk window, I get this:
In [1]: run gui-gtk.py
^CERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (126, 0))
-------
KeyboardInterrupt Traceback (most recent call last)
/home/fperez/
28 try:
29 from IPython.
---> 30 appstart_gtk()
31 except ImportError:
32 gtk.main()
/home/fperez/
163 pass
164 else:
--> 165 gtk.main()
166
167 #------
KeyboardInterrupt:
WARNING: Failure executing file: <gui-gtk.py>
I don't know if that gives you a hint as to what the problem may be. At least it allows one to recover the terminal.
Otherwise the gtk one seems fine.
- As you indicate in the docstring, it seems the gui-wx example can only be run once. Any idea why? It might be worth putting in a note requesting more info in the docstring, so at least in the future someone who knows more about this can let us know.
- Why does the Tk example actually require 'gui tk'? I would have thought that for tk, the 'do nothing' approach would be sufficient, since Python already has event loop integration with Tk built in. It might be worth dropping a note on this in the docstring and/or docs.
I think we are hitting diminishing returns here, though, and I don't want to delay things unnecessarily. You've done excellent work here, and as we saw at scipy, this is exceedingly hard to get right in all possible variations. I think we've converged on a reasonable, if not ideal, solution ...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
Just some data points on the wx polling, I just finished testing with powertop on my Ubuntu 9.04 32-bit laptop, on battery. I killed everything I could, including compiz, so I was basically running nothing but a terminal and ipython. Notes:
- Once the keyboard quiets down, my baseline is ~ 25 wakeups-from-idle per second.
- If I type
gui wx
but do NOT open an app, I see a 10Hz CPU wakeup unconditionally (via powertop). This is a python-driven wakeup and it goes away when I type 'gui', so I'm 100% sure it's the 'gui wx' call doing it.
- Once I open up a Wx app (I typed %run gui-wx from the examples), the wakeups are then controlled by the inputhook_wx. That is, they start very fast but after the fallback kicks in, they slow down to the expected level. I'll send a patch next with one extra level of fallback and some changes.
- I see that even after closing the app, the inputhook seems to continue firing until one types 'gui' to disable it. I don't know if we can auto-detect anything here to make it less active when no actual windows are open but there is still keyboard activity...
OK, I'll send my small patch now from the email interface.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
On Tue, Aug 25, 2009 at 7:09 PM, Fernando Perez<email address hidden> wrote:
>
> OK, I'll send my small patch now from the email interface.
Small patch attached, that adds an extra 'super-slow' 10-second sleep
layer, and changes the multiple-if to an elif chain, so the longer one
only kicks in after enough slow ones have passed.
I'm not necessarily proposing this change, just have a look to see if
it's any better...
1 | === modified file 'IPython/lib/inputhookwx.py' |
2 | --- IPython/lib/inputhookwx.py 2009-08-22 19:13:20 +0000 |
3 | +++ IPython/lib/inputhookwx.py 2009-08-26 01:46:56 +0000 |
4 | @@ -147,10 +147,16 @@ |
5 | # 0.005 3% |
6 | # 0.01 1.5% |
7 | # 0.05 0.5% |
8 | - if clock()-t > 1.0: |
9 | + used_time = clock() - t |
10 | + if used_time > 10.0: |
11 | + #print 'Sleep for 10 s' # dbg |
12 | + time.sleep(10.0) |
13 | + elif used_time > 1.0: |
14 | + #print 'Sleep for 1 s' # dbg |
15 | time.sleep(1.0) |
16 | - if clock()-t > 0.1: |
17 | + elif used_time > 0.1: |
18 | # Few GUI events coming in, so we can sleep longer |
19 | + #print 'Sleep for 0.05 s' # dbg |
20 | time.sleep(0.05) |
21 | else: |
22 | # Many GUI events coming in, so sleep only very little |
23 | @@ -159,4 +165,4 @@ |
24 | return 0 |
25 | |
26 | # This is our default implementation |
27 | -inputhook_wx = inputhook_wx3 |
28 | \ No newline at end of file |
29 | +inputhook_wx = inputhook_wx3 |
- 1246. By Brian Granger
-
Minor work on the gtk example.
- 1247. By Brian Granger
-
Upstream changes.
- 1248. By Brian Granger
-
Minor bug fix.
- 1249. By Brian Granger
-
Added a few more docs about GUI event loop integration.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brian Granger (ellisonbg) wrote : | # |
OK, I think I have addressed all your points and have now done the merge.
We can continue to tweak this as time goes along.
Cheers,
Brian
> - Trivial problem in gtk example:
>
> In [2]: run gui-gtk.py
> -------
> NameError Traceback (most recent call last)
>
> /home/fperez/
> <module>()
> 22 button.
> 23
> ---> 24 window.
> 25 button.show()
> 26 window.show()
>
> NameError: name 'self' is not defined
> WARNING: Failure executing file: <gui-gtk.py>
>
> I've fixed this and pushed the tiny change so I could keep my working
> branch synced; go ahead and pull before making further commits.
>
Got it.
> But I still wonder why (after the fix) this gtk example blocks completely
> ipython *after closing the gtk window*. That is, if I do *not* type '%gui
> gtk' first, and then '%run gui-gtk', the gtk window blocks the terminal, as
> it should. But even if I close the window, I can never seem to get the
> terminal back.
>
> It may be that the actual destroy event hasn't been wired, but it would be
> good to have an example that works for our users to use as reference (that
> is, one which only blocks while the window is open if they forget the %gui
> call, but that doesn't leave ipython locked forever).
>
Yep, I wired up the destroy event and it works fine now.
- As you indicate in the docstring, it seems the gui-wx example can only be
run once. Any idea why? It might be worth putting in a note requesting more
info in the docstring, so at least in the future someone who knows more
about this can let us know.
Yes, the wx window closing logic in this example is too harsh and renders wx
dead. I have added a note to the example, but I don't know wx well enough
to fix it.
- Why does the Tk example actually require 'gui tk'? I would have thought
> that for tk, the 'do nothing' approach would be sufficient, since Python
> already has event loop integration with Tk built in. It might be worth
> dropping a note on this in the docstring and/or docs.
>
Basically so we can provide a single interface to all of the different GUI
toolkits. By forcing people to "enable" (ha ha) tk, the currently active
GUI in ipython can then be proved using current_gui. At some level the same
is also true of PyQT4, which automatically handles this.
I think we are hitting diminishing returns here, though, and I don't want to
> delay things unnecessarily. You've done excellent work here, and as we saw
> at scipy, this is exceedingly hard to get right in all possible variations.
> I think we've converged on a reasonable, if not ideal, solution to this
> problem, and with help from mpl/enthought, it should be possible to make
> this transparent to all users who want pylab and/or Mayavi/Chaco/ETS.
>
> The last important thing missing here is some documentation: I think your
> solutions are fine, but we definitely need an entry in the manual (and
> possibly a FAQ entry pointing to it) explaining GUI loop integration. This
> is exteremely impo...
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Fernando Perez (fdo.perez) wrote : | # |
On Tue, Sep 1, 2009 at 9:09 AM, Brian Granger<email address hidden> wrote:
> OK, I think I have addressed all your points and have now done the merge.
> We can continue to tweak this as time goes along.
>
Awesome job, thanks! Landing this was critical, great job.
Cheers,
f
Preview Diff
1 | === modified file 'IPython/Shell.py' |
2 | --- IPython/Shell.py 2009-07-20 22:04:20 +0000 |
3 | +++ IPython/Shell.py 2009-07-21 19:34:22 +0000 |
4 | @@ -25,5 +25,5 @@ |
5 | |
6 | warn(msg, category=DeprecationWarning, stacklevel=1) |
7 | |
8 | -from IPython.core.shell import * |
9 | +from IPython.core.shell import start, IPShell, IPShellEmbed |
10 | |
11 | |
12 | === modified file 'IPython/core/shell.py' |
13 | --- IPython/core/shell.py 2009-07-02 17:46:01 +0000 |
14 | +++ IPython/core/shell.py 2009-07-21 19:28:37 +0000 |
15 | @@ -1,84 +1,61 @@ |
16 | # -*- coding: utf-8 -*- |
17 | """IPython Shell classes. |
18 | |
19 | -All the matplotlib support code was co-developed with John Hunter, |
20 | -matplotlib's author. |
21 | +Originally, this module was horribly complicated because of the need to |
22 | +use threads to integrate with GUI toolkit event loops. Now, we are using |
23 | +the :mod:`IPython.lib.inputhook`, which is based on PyOS_InputHook. This |
24 | +dramatically simplifies this logic and allow 3rd party packages (such as |
25 | +matplotlib) to handle these things by themselves. |
26 | + |
27 | +This new approach also allows projects like matplotlib to work interactively |
28 | +in the standard python shell. |
29 | """ |
30 | |
31 | -#***************************************************************************** |
32 | -# Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
33 | +#----------------------------------------------------------------------------- |
34 | +# Copyright (C) 2008-2009 The IPython Development Team |
35 | # |
36 | # Distributed under the terms of the BSD License. The full license is in |
37 | # the file COPYING, distributed as part of this software. |
38 | -#***************************************************************************** |
39 | - |
40 | -# Code begins |
41 | -# Stdlib imports |
42 | -import __builtin__ |
43 | -import __main__ |
44 | -import Queue |
45 | -import inspect |
46 | -import os |
47 | +#----------------------------------------------------------------------------- |
48 | + |
49 | +#----------------------------------------------------------------------------- |
50 | +# Imports |
51 | +#----------------------------------------------------------------------------- |
52 | + |
53 | import sys |
54 | -import thread |
55 | -import threading |
56 | -import time |
57 | - |
58 | -from signal import signal, SIGINT |
59 | - |
60 | -try: |
61 | - import ctypes |
62 | - HAS_CTYPES = True |
63 | -except ImportError: |
64 | - HAS_CTYPES = False |
65 | - |
66 | -# IPython imports |
67 | -import IPython |
68 | + |
69 | from IPython.core import ultratb |
70 | from IPython.core import ipapi |
71 | -from IPython.core.magic import Magic |
72 | -from IPython.utils.genutils import Term,warn,error,flag_calls, ask_yes_no |
73 | +from IPython.utils.genutils import ask_yes_no |
74 | from IPython.core.iplib import InteractiveShell |
75 | from IPython.core.ipmaker import make_IPython |
76 | -from IPython.utils.ipstruct import Struct |
77 | -from IPython.testing import decorators as testdec |
78 | - |
79 | -# Globals |
80 | -# global flag to pass around information about Ctrl-C without exceptions |
81 | -KBINT = False |
82 | - |
83 | -# global flag to turn on/off Tk support. |
84 | -USE_TK = False |
85 | - |
86 | -# ID for the main thread, used for cross-thread exceptions |
87 | -MAIN_THREAD_ID = thread.get_ident() |
88 | - |
89 | -# Tag when runcode() is active, for exception handling |
90 | -CODE_RUN = None |
91 | - |
92 | -# Default timeout for waiting for multithreaded shells (in seconds) |
93 | -GUI_TIMEOUT = 10 |
94 | - |
95 | -#----------------------------------------------------------------------------- |
96 | -# This class is trivial now, but I want to have it in to publish a clean |
97 | -# interface. Later when the internals are reorganized, code that uses this |
98 | -# shouldn't have to change. |
99 | + |
100 | +#----------------------------------------------------------------------------- |
101 | +# Code |
102 | +#----------------------------------------------------------------------------- |
103 | + |
104 | |
105 | class IPShell: |
106 | - """Create an IPython instance.""" |
107 | - |
108 | - def __init__(self,argv=None,user_ns=None,user_global_ns=None, |
109 | - debug=1,shell_class=InteractiveShell): |
110 | - self.IP = make_IPython(argv,user_ns=user_ns, |
111 | + """Create an IPython instance. |
112 | + |
113 | + This calls the factory :func:`make_IPython`, which creates a configured |
114 | + :class:`InteractiveShell` object, and presents the result as a simple |
115 | + class with a :meth:`mainloop` method. |
116 | + """ |
117 | + |
118 | + def __init__(self, argv=None, user_ns=None, user_global_ns=None, |
119 | + debug=1, shell_class=InteractiveShell): |
120 | + self.IP = make_IPython(argv, user_ns=user_ns, |
121 | user_global_ns=user_global_ns, |
122 | - debug=debug,shell_class=shell_class) |
123 | + debug=debug, shell_class=shell_class) |
124 | |
125 | def mainloop(self,sys_exit=0,banner=None): |
126 | self.IP.mainloop(banner) |
127 | if sys_exit: |
128 | sys.exit() |
129 | |
130 | -#----------------------------------------------------------------------------- |
131 | + |
132 | +# This is an additional magic that is exposed in embedded shells. |
133 | def kill_embedded(self,parameter_s=''): |
134 | """%kill_embedded : deactivate for good the current embedded IPython. |
135 | |
136 | @@ -94,33 +71,34 @@ |
137 | if kill: |
138 | self.shell.embedded_active = False |
139 | print "This embedded IPython will not reactivate anymore once you exit." |
140 | - |
141 | + |
142 | + |
143 | class IPShellEmbed: |
144 | """Allow embedding an IPython shell into a running program. |
145 | |
146 | Instances of this class are callable, with the __call__ method being an |
147 | alias to the embed() method of an InteractiveShell instance. |
148 | |
149 | - Usage (see also the example-embed.py file for a running example): |
150 | - |
151 | - ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override]) |
152 | - |
153 | - - argv: list containing valid command-line options for IPython, as they |
154 | + Usage (see also the example-embed.py file for a running example):: |
155 | + |
156 | + ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override]) |
157 | + |
158 | + * argv: list containing valid command-line options for IPython, as they |
159 | would appear in sys.argv[1:]. |
160 | |
161 | - For example, the following command-line options: |
162 | - |
163 | - $ ipython -prompt_in1 'Input <\\#>' -colors LightBG |
164 | - |
165 | - would be passed in the argv list as: |
166 | - |
167 | - ['-prompt_in1','Input <\\#>','-colors','LightBG'] |
168 | - |
169 | - - banner: string which gets printed every time the interpreter starts. |
170 | - |
171 | - - exit_msg: string which gets printed every time the interpreter exits. |
172 | - |
173 | - - rc_override: a dict or Struct of configuration options such as those |
174 | + For example, the following command-line options:: |
175 | + |
176 | + $ ipython -prompt_in1 'Input <\\#>' -colors LightBG |
177 | + |
178 | + would be passed in the argv list as:: |
179 | + |
180 | + ['-prompt_in1','Input <\\#>','-colors','LightBG'] |
181 | + |
182 | + * banner: string which gets printed every time the interpreter starts. |
183 | + |
184 | + * exit_msg: string which gets printed every time the interpreter exits. |
185 | + |
186 | + * rc_override: a dict or Struct of configuration options such as those |
187 | used by IPython. These options are read from your ~/.ipython/ipythonrc |
188 | file when the Shell object is created. Passing an explicit rc_override |
189 | dict with any options you want allows you to override those values at |
190 | @@ -129,11 +107,11 @@ |
191 | global files (thus keeping your interactive IPython configuration |
192 | unchanged). |
193 | |
194 | - Then the ipshell instance can be called anywhere inside your code: |
195 | + Then the ipshell instance can be called anywhere inside your code:: |
196 | |
197 | - ipshell(header='') -> Opens up an IPython shell. |
198 | + ipshell(header='') -> Opens up an IPython shell. |
199 | |
200 | - - header: string printed by the IPython shell upon startup. This can let |
201 | + * header: string printed by the IPython shell upon startup. This can let |
202 | you know where in your code you are when dropping into the shell. Note |
203 | that 'banner' gets prepended to all calls, so header is used for |
204 | location-specific information. |
205 | @@ -145,11 +123,13 @@ |
206 | |
207 | This functionality was inspired by a posting on comp.lang.python by cmkl |
208 | <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and |
209 | - by the IDL stop/continue commands.""" |
210 | + by the IDL stop/continue commands. |
211 | + """ |
212 | |
213 | - def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None, |
214 | - user_ns=None): |
215 | + def __init__(self, argv=None, banner='', exit_msg=None, |
216 | + rc_override=None, user_ns=None): |
217 | """Note that argv here is a string, NOT a list.""" |
218 | + |
219 | self.set_banner(banner) |
220 | self.set_exit_msg(exit_msg) |
221 | self.set_dummy_mode(0) |
222 | @@ -196,7 +176,7 @@ |
223 | except: |
224 | pass |
225 | |
226 | - def __call__(self,header='',local_ns=None,global_ns=None,dummy=None): |
227 | + def __call__(self, header='', local_ns=None, global_ns=None, dummy=None): |
228 | """Activate the interactive interpreter. |
229 | |
230 | __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start |
231 | @@ -213,7 +193,8 @@ |
232 | can still have a specific call work by making it as IPShell(dummy=0). |
233 | |
234 | The optional keyword parameter dummy controls whether the call |
235 | - actually does anything. """ |
236 | + actually does anything. |
237 | + """ |
238 | |
239 | # If the user has turned it off, go away |
240 | if not self.IP.embedded_active: |
241 | @@ -249,7 +230,7 @@ |
242 | sys.displayhook = self.sys_displayhook_ori |
243 | self.restore_system_completer() |
244 | |
245 | - def set_dummy_mode(self,dummy): |
246 | + def set_dummy_mode(self, dummy): |
247 | """Sets the embeddable shell's dummy mode parameter. |
248 | |
249 | set_dummy_mode(dummy): dummy = 0 or 1. |
250 | @@ -269,7 +250,7 @@ |
251 | """ |
252 | return self.__dummy_mode |
253 | |
254 | - def set_banner(self,banner): |
255 | + def set_banner(self, banner): |
256 | """Sets the global banner. |
257 | |
258 | This banner gets prepended to every header printed when the shell |
259 | @@ -277,7 +258,7 @@ |
260 | |
261 | self.banner = banner |
262 | |
263 | - def set_exit_msg(self,exit_msg): |
264 | + def set_exit_msg(self, exit_msg): |
265 | """Sets the global exit_msg. |
266 | |
267 | This exit message gets printed upon exiting every time the embedded |
268 | @@ -285,963 +266,8 @@ |
269 | |
270 | self.exit_msg = exit_msg |
271 | |
272 | -#----------------------------------------------------------------------------- |
273 | -if HAS_CTYPES: |
274 | - # Add async exception support. Trick taken from: |
275 | - # http://sebulba.wikispaces.com/recipe+thread2 |
276 | - def _async_raise(tid, exctype): |
277 | - """raises the exception, performs cleanup if needed""" |
278 | - if not inspect.isclass(exctype): |
279 | - raise TypeError("Only types can be raised (not instances)") |
280 | - # Explicit cast to c_long is necessary for 64-bit support: |
281 | - # See https://bugs.launchpad.net/ipython/+bug/237073 |
282 | - res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), |
283 | - ctypes.py_object(exctype)) |
284 | - if res == 0: |
285 | - raise ValueError("invalid thread id") |
286 | - elif res != 1: |
287 | - # If it returns a number greater than one, you're in trouble, |
288 | - # and you should call it again with exc=NULL to revert the effect |
289 | - ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) |
290 | - raise SystemError("PyThreadState_SetAsyncExc failed") |
291 | - |
292 | - def sigint_handler(signum,stack_frame): |
293 | - """Sigint handler for threaded apps. |
294 | - |
295 | - This is a horrible hack to pass information about SIGINT _without_ |
296 | - using exceptions, since I haven't been able to properly manage |
297 | - cross-thread exceptions in GTK/WX. In fact, I don't think it can be |
298 | - done (or at least that's my understanding from a c.l.py thread where |
299 | - this was discussed).""" |
300 | - |
301 | - global KBINT |
302 | - |
303 | - if CODE_RUN: |
304 | - _async_raise(MAIN_THREAD_ID,KeyboardInterrupt) |
305 | - else: |
306 | - KBINT = True |
307 | - print '\nKeyboardInterrupt - Press <Enter> to continue.', |
308 | - Term.cout.flush() |
309 | - |
310 | -else: |
311 | - def sigint_handler(signum,stack_frame): |
312 | - """Sigint handler for threaded apps. |
313 | - |
314 | - This is a horrible hack to pass information about SIGINT _without_ |
315 | - using exceptions, since I haven't been able to properly manage |
316 | - cross-thread exceptions in GTK/WX. In fact, I don't think it can be |
317 | - done (or at least that's my understanding from a c.l.py thread where |
318 | - this was discussed).""" |
319 | - |
320 | - global KBINT |
321 | - |
322 | - print '\nKeyboardInterrupt - Press <Enter> to continue.', |
323 | - Term.cout.flush() |
324 | - # Set global flag so that runsource can know that Ctrl-C was hit |
325 | - KBINT = True |
326 | - |
327 | - |
328 | -class MTInteractiveShell(InteractiveShell): |
329 | - """Simple multi-threaded shell.""" |
330 | - |
331 | - # Threading strategy taken from: |
332 | - # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian |
333 | - # McErlean and John Finlay. Modified with corrections by Antoon Pardon, |
334 | - # from the pygtk mailing list, to avoid lockups with system calls. |
335 | - |
336 | - # class attribute to indicate whether the class supports threads or not. |
337 | - # Subclasses with thread support should override this as needed. |
338 | - isthreaded = True |
339 | - |
340 | - def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), |
341 | - user_ns=None,user_global_ns=None,banner2='', |
342 | - gui_timeout=GUI_TIMEOUT,**kw): |
343 | - """Similar to the normal InteractiveShell, but with threading control""" |
344 | - |
345 | - InteractiveShell.__init__(self,name,usage,rc,user_ns, |
346 | - user_global_ns,banner2) |
347 | - |
348 | - # Timeout we wait for GUI thread |
349 | - self.gui_timeout = gui_timeout |
350 | - |
351 | - # A queue to hold the code to be executed. |
352 | - self.code_queue = Queue.Queue() |
353 | - |
354 | - # Stuff to do at closing time |
355 | - self._kill = None |
356 | - on_kill = kw.get('on_kill', []) |
357 | - # Check that all things to kill are callable: |
358 | - for t in on_kill: |
359 | - if not callable(t): |
360 | - raise TypeError,'on_kill must be a list of callables' |
361 | - self.on_kill = on_kill |
362 | - # thread identity of the "worker thread" (that may execute code directly) |
363 | - self.worker_ident = None |
364 | - |
365 | - def runsource(self, source, filename="<input>", symbol="single"): |
366 | - """Compile and run some source in the interpreter. |
367 | - |
368 | - Modified version of code.py's runsource(), to handle threading issues. |
369 | - See the original for full docstring details.""" |
370 | - |
371 | - global KBINT |
372 | - |
373 | - # If Ctrl-C was typed, we reset the flag and return right away |
374 | - if KBINT: |
375 | - KBINT = False |
376 | - return False |
377 | - |
378 | - if self._kill: |
379 | - # can't queue new code if we are being killed |
380 | - return True |
381 | - |
382 | - try: |
383 | - code = self.compile(source, filename, symbol) |
384 | - except (OverflowError, SyntaxError, ValueError): |
385 | - # Case 1 |
386 | - self.showsyntaxerror(filename) |
387 | - return False |
388 | - |
389 | - if code is None: |
390 | - # Case 2 |
391 | - return True |
392 | - |
393 | - # shortcut - if we are in worker thread, or the worker thread is not |
394 | - # running, execute directly (to allow recursion and prevent deadlock if |
395 | - # code is run early in IPython construction) |
396 | - |
397 | - if (self.worker_ident is None |
398 | - or self.worker_ident == thread.get_ident() ): |
399 | - InteractiveShell.runcode(self,code) |
400 | - return False |
401 | - |
402 | - # Case 3 |
403 | - # Store code in queue, so the execution thread can handle it. |
404 | - |
405 | - completed_ev, received_ev = threading.Event(), threading.Event() |
406 | - |
407 | - self.code_queue.put((code,completed_ev, received_ev)) |
408 | - # first make sure the message was received, with timeout |
409 | - received_ev.wait(self.gui_timeout) |
410 | - if not received_ev.isSet(): |
411 | - # the mainloop is dead, start executing code directly |
412 | - print "Warning: Timeout for mainloop thread exceeded" |
413 | - print "switching to nonthreaded mode (until mainloop wakes up again)" |
414 | - self.worker_ident = None |
415 | - else: |
416 | - completed_ev.wait() |
417 | - return False |
418 | - |
419 | - def runcode(self): |
420 | - """Execute a code object. |
421 | - |
422 | - Multithreaded wrapper around IPython's runcode().""" |
423 | - |
424 | - global CODE_RUN |
425 | - |
426 | - # we are in worker thread, stash out the id for runsource() |
427 | - self.worker_ident = thread.get_ident() |
428 | - |
429 | - if self._kill: |
430 | - print >>Term.cout, 'Closing threads...', |
431 | - Term.cout.flush() |
432 | - for tokill in self.on_kill: |
433 | - tokill() |
434 | - print >>Term.cout, 'Done.' |
435 | - # allow kill() to return |
436 | - self._kill.set() |
437 | - return True |
438 | - |
439 | - # Install sigint handler. We do it every time to ensure that if user |
440 | - # code modifies it, we restore our own handling. |
441 | - try: |
442 | - signal(SIGINT,sigint_handler) |
443 | - except SystemError: |
444 | - # This happens under Windows, which seems to have all sorts |
445 | - # of problems with signal handling. Oh well... |
446 | - pass |
447 | - |
448 | - # Flush queue of pending code by calling the run methood of the parent |
449 | - # class with all items which may be in the queue. |
450 | - code_to_run = None |
451 | - while 1: |
452 | - try: |
453 | - code_to_run, completed_ev, received_ev = self.code_queue.get_nowait() |
454 | - except Queue.Empty: |
455 | - break |
456 | - received_ev.set() |
457 | - |
458 | - # Exceptions need to be raised differently depending on which |
459 | - # thread is active. This convoluted try/except is only there to |
460 | - # protect against asynchronous exceptions, to ensure that a KBINT |
461 | - # at the wrong time doesn't deadlock everything. The global |
462 | - # CODE_TO_RUN is set to true/false as close as possible to the |
463 | - # runcode() call, so that the KBINT handler is correctly informed. |
464 | - try: |
465 | - try: |
466 | - CODE_RUN = True |
467 | - InteractiveShell.runcode(self,code_to_run) |
468 | - except KeyboardInterrupt: |
469 | - print "Keyboard interrupted in mainloop" |
470 | - while not self.code_queue.empty(): |
471 | - code, ev1,ev2 = self.code_queue.get_nowait() |
472 | - ev1.set() |
473 | - ev2.set() |
474 | - break |
475 | - finally: |
476 | - CODE_RUN = False |
477 | - # allow runsource() return from wait |
478 | - completed_ev.set() |
479 | - |
480 | - |
481 | - # This MUST return true for gtk threading to work |
482 | - return True |
483 | - |
484 | - def kill(self): |
485 | - """Kill the thread, returning when it has been shut down.""" |
486 | - self._kill = threading.Event() |
487 | - self._kill.wait() |
488 | - |
489 | -class MatplotlibShellBase: |
490 | - """Mixin class to provide the necessary modifications to regular IPython |
491 | - shell classes for matplotlib support. |
492 | - |
493 | - Given Python's MRO, this should be used as the FIRST class in the |
494 | - inheritance hierarchy, so that it overrides the relevant methods.""" |
495 | - |
496 | - def _matplotlib_config(self,name,user_ns,user_global_ns=None): |
497 | - """Return items needed to setup the user's shell with matplotlib""" |
498 | - |
499 | - # Initialize matplotlib to interactive mode always |
500 | - import matplotlib |
501 | - from matplotlib import backends |
502 | - matplotlib.interactive(True) |
503 | - |
504 | - def use(arg): |
505 | - """IPython wrapper for matplotlib's backend switcher. |
506 | - |
507 | - In interactive use, we can not allow switching to a different |
508 | - interactive backend, since thread conflicts will most likely crash |
509 | - the python interpreter. This routine does a safety check first, |
510 | - and refuses to perform a dangerous switch. It still allows |
511 | - switching to non-interactive backends.""" |
512 | - |
513 | - if arg in backends.interactive_bk and arg != self.mpl_backend: |
514 | - m=('invalid matplotlib backend switch.\n' |
515 | - 'This script attempted to switch to the interactive ' |
516 | - 'backend: `%s`\n' |
517 | - 'Your current choice of interactive backend is: `%s`\n\n' |
518 | - 'Switching interactive matplotlib backends at runtime\n' |
519 | - 'would crash the python interpreter, ' |
520 | - 'and IPython has blocked it.\n\n' |
521 | - 'You need to either change your choice of matplotlib backend\n' |
522 | - 'by editing your .matplotlibrc file, or run this script as a \n' |
523 | - 'standalone file from the command line, not using IPython.\n' % |
524 | - (arg,self.mpl_backend) ) |
525 | - raise RuntimeError, m |
526 | - else: |
527 | - self.mpl_use(arg) |
528 | - self.mpl_use._called = True |
529 | - |
530 | - self.matplotlib = matplotlib |
531 | - self.mpl_backend = matplotlib.rcParams['backend'] |
532 | - |
533 | - # we also need to block switching of interactive backends by use() |
534 | - self.mpl_use = matplotlib.use |
535 | - self.mpl_use._called = False |
536 | - # overwrite the original matplotlib.use with our wrapper |
537 | - matplotlib.use = use |
538 | - |
539 | - # This must be imported last in the matplotlib series, after |
540 | - # backend/interactivity choices have been made |
541 | - import matplotlib.pylab as pylab |
542 | - self.pylab = pylab |
543 | - |
544 | - self.pylab.show._needmain = False |
545 | - # We need to detect at runtime whether show() is called by the user. |
546 | - # For this, we wrap it into a decorator which adds a 'called' flag. |
547 | - self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive) |
548 | - |
549 | - # Build a user namespace initialized with matplotlib/matlab features. |
550 | - user_ns, user_global_ns = ipapi.make_user_namespaces(user_ns, |
551 | - user_global_ns) |
552 | - |
553 | - # Import numpy as np/pyplot as plt are conventions we're trying to |
554 | - # somewhat standardize on. Making them available to users by default |
555 | - # will greatly help this. |
556 | - exec ("import numpy\n" |
557 | - "import numpy as np\n" |
558 | - "import matplotlib\n" |
559 | - "import matplotlib.pylab as pylab\n" |
560 | - "try:\n" |
561 | - " import matplotlib.pyplot as plt\n" |
562 | - "except ImportError:\n" |
563 | - " pass\n" |
564 | - ) in user_ns |
565 | - |
566 | - # Build matplotlib info banner |
567 | - b=""" |
568 | - Welcome to pylab, a matplotlib-based Python environment. |
569 | - For more information, type 'help(pylab)'. |
570 | -""" |
571 | - return user_ns,user_global_ns,b |
572 | - |
573 | - def mplot_exec(self,fname,*where,**kw): |
574 | - """Execute a matplotlib script. |
575 | - |
576 | - This is a call to execfile(), but wrapped in safeties to properly |
577 | - handle interactive rendering and backend switching.""" |
578 | - |
579 | - #print '*** Matplotlib runner ***' # dbg |
580 | - # turn off rendering until end of script |
581 | - isInteractive = self.matplotlib.rcParams['interactive'] |
582 | - self.matplotlib.interactive(False) |
583 | - self.safe_execfile(fname,*where,**kw) |
584 | - self.matplotlib.interactive(isInteractive) |
585 | - # make rendering call now, if the user tried to do it |
586 | - if self.pylab.draw_if_interactive.called: |
587 | - self.pylab.draw() |
588 | - self.pylab.draw_if_interactive.called = False |
589 | - |
590 | - # if a backend switch was performed, reverse it now |
591 | - if self.mpl_use._called: |
592 | - self.matplotlib.rcParams['backend'] = self.mpl_backend |
593 | - |
594 | - @testdec.skip_doctest |
595 | - def magic_run(self,parameter_s=''): |
596 | - Magic.magic_run(self,parameter_s,runner=self.mplot_exec) |
597 | - |
598 | - # Fix the docstring so users see the original as well |
599 | - magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__, |
600 | - "\n *** Modified %run for Matplotlib," |
601 | - " with proper interactive handling ***") |
602 | - |
603 | -# Now we provide 2 versions of a matplotlib-aware IPython base shells, single |
604 | -# and multithreaded. Note that these are meant for internal use, the IPShell* |
605 | -# classes below are the ones meant for public consumption. |
606 | - |
607 | -class MatplotlibShell(MatplotlibShellBase,InteractiveShell): |
608 | - """Single-threaded shell with matplotlib support.""" |
609 | - |
610 | - def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), |
611 | - user_ns=None,user_global_ns=None,**kw): |
612 | - user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns) |
613 | - InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns, |
614 | - banner2=b2,**kw) |
615 | - |
616 | -class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell): |
617 | - """Multi-threaded shell with matplotlib support.""" |
618 | - |
619 | - def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), |
620 | - user_ns=None,user_global_ns=None, **kw): |
621 | - user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns) |
622 | - MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns, |
623 | - banner2=b2,**kw) |
624 | - |
625 | -#----------------------------------------------------------------------------- |
626 | -# Utility functions for the different GUI enabled IPShell* classes. |
627 | - |
628 | -def get_tk(): |
629 | - """Tries to import Tkinter and returns a withdrawn Tkinter root |
630 | - window. If Tkinter is already imported or not available, this |
631 | - returns None. This function calls `hijack_tk` underneath. |
632 | - """ |
633 | - if not USE_TK or sys.modules.has_key('Tkinter'): |
634 | - return None |
635 | - else: |
636 | - try: |
637 | - import Tkinter |
638 | - except ImportError: |
639 | - return None |
640 | - else: |
641 | - hijack_tk() |
642 | - r = Tkinter.Tk() |
643 | - r.withdraw() |
644 | - return r |
645 | - |
646 | -def hijack_tk(): |
647 | - """Modifies Tkinter's mainloop with a dummy so when a module calls |
648 | - mainloop, it does not block. |
649 | - |
650 | - """ |
651 | - def misc_mainloop(self, n=0): |
652 | - pass |
653 | - def tkinter_mainloop(n=0): |
654 | - pass |
655 | - |
656 | - import Tkinter |
657 | - Tkinter.Misc.mainloop = misc_mainloop |
658 | - Tkinter.mainloop = tkinter_mainloop |
659 | - |
660 | -def update_tk(tk): |
661 | - """Updates the Tkinter event loop. This is typically called from |
662 | - the respective WX or GTK mainloops. |
663 | - """ |
664 | - if tk: |
665 | - tk.update() |
666 | - |
667 | -def hijack_wx(): |
668 | - """Modifies wxPython's MainLoop with a dummy so user code does not |
669 | - block IPython. The hijacked mainloop function is returned. |
670 | - """ |
671 | - def dummy_mainloop(*args, **kw): |
672 | - pass |
673 | - |
674 | - try: |
675 | - import wx |
676 | - except ImportError: |
677 | - # For very old versions of WX |
678 | - import wxPython as wx |
679 | - |
680 | - ver = wx.__version__ |
681 | - orig_mainloop = None |
682 | - if ver[:3] >= '2.5': |
683 | - import wx |
684 | - if hasattr(wx, '_core_'): core = getattr(wx, '_core_') |
685 | - elif hasattr(wx, '_core'): core = getattr(wx, '_core') |
686 | - else: raise AttributeError('Could not find wx core module') |
687 | - orig_mainloop = core.PyApp_MainLoop |
688 | - core.PyApp_MainLoop = dummy_mainloop |
689 | - elif ver[:3] == '2.4': |
690 | - orig_mainloop = wx.wxc.wxPyApp_MainLoop |
691 | - wx.wxc.wxPyApp_MainLoop = dummy_mainloop |
692 | - else: |
693 | - warn("Unable to find either wxPython version 2.4 or >= 2.5.") |
694 | - return orig_mainloop |
695 | - |
696 | -def hijack_gtk(): |
697 | - """Modifies pyGTK's mainloop with a dummy so user code does not |
698 | - block IPython. This function returns the original `gtk.mainloop` |
699 | - function that has been hijacked. |
700 | - """ |
701 | - def dummy_mainloop(*args, **kw): |
702 | - pass |
703 | - import gtk |
704 | - if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main |
705 | - else: orig_mainloop = gtk.mainloop |
706 | - gtk.mainloop = dummy_mainloop |
707 | - gtk.main = dummy_mainloop |
708 | - return orig_mainloop |
709 | - |
710 | -def hijack_qt(): |
711 | - """Modifies PyQt's mainloop with a dummy so user code does not |
712 | - block IPython. This function returns the original |
713 | - `qt.qApp.exec_loop` function that has been hijacked. |
714 | - """ |
715 | - def dummy_mainloop(*args, **kw): |
716 | - pass |
717 | - import qt |
718 | - orig_mainloop = qt.qApp.exec_loop |
719 | - qt.qApp.exec_loop = dummy_mainloop |
720 | - qt.QApplication.exec_loop = dummy_mainloop |
721 | - return orig_mainloop |
722 | - |
723 | -def hijack_qt4(): |
724 | - """Modifies PyQt4's mainloop with a dummy so user code does not |
725 | - block IPython. This function returns the original |
726 | - `QtGui.qApp.exec_` function that has been hijacked. |
727 | - """ |
728 | - def dummy_mainloop(*args, **kw): |
729 | - pass |
730 | - from PyQt4 import QtGui, QtCore |
731 | - orig_mainloop = QtGui.qApp.exec_ |
732 | - QtGui.qApp.exec_ = dummy_mainloop |
733 | - QtGui.QApplication.exec_ = dummy_mainloop |
734 | - QtCore.QCoreApplication.exec_ = dummy_mainloop |
735 | - return orig_mainloop |
736 | - |
737 | -#----------------------------------------------------------------------------- |
738 | -# The IPShell* classes below are the ones meant to be run by external code as |
739 | -# IPython instances. Note that unless a specific threading strategy is |
740 | -# desired, the factory function start() below should be used instead (it |
741 | -# selects the proper threaded class). |
742 | - |
743 | -class IPThread(threading.Thread): |
744 | - def run(self): |
745 | - self.IP.mainloop(self._banner) |
746 | - self.IP.kill() |
747 | - |
748 | -class IPShellGTK(IPThread): |
749 | - """Run a gtk mainloop() in a separate thread. |
750 | - |
751 | - Python commands can be passed to the thread where they will be executed. |
752 | - This is implemented by periodically checking for passed code using a |
753 | - GTK timeout callback.""" |
754 | - |
755 | - TIMEOUT = 100 # Millisecond interval between timeouts. |
756 | - |
757 | - def __init__(self,argv=None,user_ns=None,user_global_ns=None, |
758 | - debug=1,shell_class=MTInteractiveShell): |
759 | - |
760 | - import gtk |
761 | - # Check for set_interactive, coming up in new pygtk. |
762 | - # Disable it so that this code works, but notify |
763 | - # the user that he has a better option as well. |
764 | - # XXX TODO better support when set_interactive is released |
765 | - try: |
766 | - gtk.set_interactive(False) |
767 | - print "Your PyGtk has set_interactive(), so you can use the" |
768 | - print "more stable single-threaded Gtk mode." |
769 | - print "See https://bugs.launchpad.net/ipython/+bug/270856" |
770 | - except AttributeError: |
771 | - pass |
772 | - |
773 | - self.gtk = gtk |
774 | - self.gtk_mainloop = hijack_gtk() |
775 | - |
776 | - # Allows us to use both Tk and GTK. |
777 | - self.tk = get_tk() |
778 | - |
779 | - if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit |
780 | - else: mainquit = self.gtk.mainquit |
781 | - |
782 | - self.IP = make_IPython(argv,user_ns=user_ns, |
783 | - user_global_ns=user_global_ns, |
784 | - debug=debug, |
785 | - shell_class=shell_class, |
786 | - on_kill=[mainquit]) |
787 | - |
788 | - # HACK: slot for banner in self; it will be passed to the mainloop |
789 | - # method only and .run() needs it. The actual value will be set by |
790 | - # .mainloop(). |
791 | - self._banner = None |
792 | - |
793 | - threading.Thread.__init__(self) |
794 | - |
795 | - def mainloop(self,sys_exit=0,banner=None): |
796 | - |
797 | - self._banner = banner |
798 | - |
799 | - if self.gtk.pygtk_version >= (2,4,0): |
800 | - import gobject |
801 | - gobject.idle_add(self.on_timer) |
802 | - else: |
803 | - self.gtk.idle_add(self.on_timer) |
804 | - |
805 | - if sys.platform != 'win32': |
806 | - try: |
807 | - if self.gtk.gtk_version[0] >= 2: |
808 | - self.gtk.gdk.threads_init() |
809 | - except AttributeError: |
810 | - pass |
811 | - except RuntimeError: |
812 | - error('Your pyGTK likely has not been compiled with ' |
813 | - 'threading support.\n' |
814 | - 'The exception printout is below.\n' |
815 | - 'You can either rebuild pyGTK with threads, or ' |
816 | - 'try using \n' |
817 | - 'matplotlib with a different backend (like Tk or WX).\n' |
818 | - 'Note that matplotlib will most likely not work in its ' |
819 | - 'current state!') |
820 | - self.IP.InteractiveTB() |
821 | - |
822 | - self.start() |
823 | - self.gtk.gdk.threads_enter() |
824 | - self.gtk_mainloop() |
825 | - self.gtk.gdk.threads_leave() |
826 | - self.join() |
827 | - |
828 | - def on_timer(self): |
829 | - """Called when GTK is idle. |
830 | - |
831 | - Must return True always, otherwise GTK stops calling it""" |
832 | - |
833 | - update_tk(self.tk) |
834 | - self.IP.runcode() |
835 | - time.sleep(0.01) |
836 | - return True |
837 | - |
838 | - |
839 | -class IPShellWX(IPThread): |
840 | - """Run a wx mainloop() in a separate thread. |
841 | - |
842 | - Python commands can be passed to the thread where they will be executed. |
843 | - This is implemented by periodically checking for passed code using a |
844 | - GTK timeout callback.""" |
845 | - |
846 | - TIMEOUT = 100 # Millisecond interval between timeouts. |
847 | - |
848 | - def __init__(self,argv=None,user_ns=None,user_global_ns=None, |
849 | - debug=1,shell_class=MTInteractiveShell): |
850 | - |
851 | - self.IP = make_IPython(argv,user_ns=user_ns, |
852 | - user_global_ns=user_global_ns, |
853 | - debug=debug, |
854 | - shell_class=shell_class, |
855 | - on_kill=[self.wxexit]) |
856 | - |
857 | - wantedwxversion=self.IP.rc.wxversion |
858 | - if wantedwxversion!="0": |
859 | - try: |
860 | - import wxversion |
861 | - except ImportError: |
862 | - error('The wxversion module is needed for WX version selection') |
863 | - else: |
864 | - try: |
865 | - wxversion.select(wantedwxversion) |
866 | - except: |
867 | - self.IP.InteractiveTB() |
868 | - error('Requested wxPython version %s could not be loaded' % |
869 | - wantedwxversion) |
870 | - |
871 | - import wx |
872 | - |
873 | - threading.Thread.__init__(self) |
874 | - self.wx = wx |
875 | - self.wx_mainloop = hijack_wx() |
876 | - |
877 | - # Allows us to use both Tk and GTK. |
878 | - self.tk = get_tk() |
879 | - |
880 | - # HACK: slot for banner in self; it will be passed to the mainloop |
881 | - # method only and .run() needs it. The actual value will be set by |
882 | - # .mainloop(). |
883 | - self._banner = None |
884 | - |
885 | - self.app = None |
886 | - |
887 | - def wxexit(self, *args): |
888 | - if self.app is not None: |
889 | - self.app.agent.timer.Stop() |
890 | - self.app.ExitMainLoop() |
891 | - |
892 | - def mainloop(self,sys_exit=0,banner=None): |
893 | - |
894 | - self._banner = banner |
895 | - |
896 | - self.start() |
897 | - |
898 | - class TimerAgent(self.wx.MiniFrame): |
899 | - wx = self.wx |
900 | - IP = self.IP |
901 | - tk = self.tk |
902 | - def __init__(self, parent, interval): |
903 | - style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ |
904 | - self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200), |
905 | - size=(100, 100),style=style) |
906 | - self.Show(False) |
907 | - self.interval = interval |
908 | - self.timerId = self.wx.NewId() |
909 | - |
910 | - def StartWork(self): |
911 | - self.timer = self.wx.Timer(self, self.timerId) |
912 | - self.wx.EVT_TIMER(self, self.timerId, self.OnTimer) |
913 | - self.timer.Start(self.interval) |
914 | - |
915 | - def OnTimer(self, event): |
916 | - update_tk(self.tk) |
917 | - self.IP.runcode() |
918 | - |
919 | - class App(self.wx.App): |
920 | - wx = self.wx |
921 | - TIMEOUT = self.TIMEOUT |
922 | - def OnInit(self): |
923 | - 'Create the main window and insert the custom frame' |
924 | - self.agent = TimerAgent(None, self.TIMEOUT) |
925 | - self.agent.Show(False) |
926 | - self.agent.StartWork() |
927 | - return True |
928 | - |
929 | - self.app = App(redirect=False) |
930 | - self.wx_mainloop(self.app) |
931 | - self.join() |
932 | - |
933 | - |
934 | -class IPShellQt(IPThread): |
935 | - """Run a Qt event loop in a separate thread. |
936 | - |
937 | - Python commands can be passed to the thread where they will be executed. |
938 | - This is implemented by periodically checking for passed code using a |
939 | - Qt timer / slot.""" |
940 | - |
941 | - TIMEOUT = 100 # Millisecond interval between timeouts. |
942 | - |
943 | - def __init__(self, argv=None, user_ns=None, user_global_ns=None, |
944 | - debug=0, shell_class=MTInteractiveShell): |
945 | - |
946 | - import qt |
947 | - |
948 | - self.exec_loop = hijack_qt() |
949 | - |
950 | - # Allows us to use both Tk and QT. |
951 | - self.tk = get_tk() |
952 | - |
953 | - self.IP = make_IPython(argv, |
954 | - user_ns=user_ns, |
955 | - user_global_ns=user_global_ns, |
956 | - debug=debug, |
957 | - shell_class=shell_class, |
958 | - on_kill=[qt.qApp.exit]) |
959 | - |
960 | - # HACK: slot for banner in self; it will be passed to the mainloop |
961 | - # method only and .run() needs it. The actual value will be set by |
962 | - # .mainloop(). |
963 | - self._banner = None |
964 | - |
965 | - threading.Thread.__init__(self) |
966 | - |
967 | - def mainloop(self, sys_exit=0, banner=None): |
968 | - |
969 | - import qt |
970 | - |
971 | - self._banner = banner |
972 | - |
973 | - if qt.QApplication.startingUp(): |
974 | - a = qt.QApplication(sys.argv) |
975 | - |
976 | - self.timer = qt.QTimer() |
977 | - qt.QObject.connect(self.timer, |
978 | - qt.SIGNAL('timeout()'), |
979 | - self.on_timer) |
980 | - |
981 | - self.start() |
982 | - self.timer.start(self.TIMEOUT, True) |
983 | - while True: |
984 | - if self.IP._kill: break |
985 | - self.exec_loop() |
986 | - self.join() |
987 | - |
988 | - def on_timer(self): |
989 | - update_tk(self.tk) |
990 | - result = self.IP.runcode() |
991 | - self.timer.start(self.TIMEOUT, True) |
992 | - return result |
993 | - |
994 | - |
995 | -class IPShellQt4(IPThread): |
996 | - """Run a Qt event loop in a separate thread. |
997 | - |
998 | - Python commands can be passed to the thread where they will be executed. |
999 | - This is implemented by periodically checking for passed code using a |
1000 | - Qt timer / slot.""" |
1001 | - |
1002 | - TIMEOUT = 100 # Millisecond interval between timeouts. |
1003 | - |
1004 | - def __init__(self, argv=None, user_ns=None, user_global_ns=None, |
1005 | - debug=0, shell_class=MTInteractiveShell): |
1006 | - |
1007 | - from PyQt4 import QtCore, QtGui |
1008 | - |
1009 | - try: |
1010 | - # present in PyQt4-4.2.1 or later |
1011 | - QtCore.pyqtRemoveInputHook() |
1012 | - except AttributeError: |
1013 | - pass |
1014 | - |
1015 | - if QtCore.PYQT_VERSION_STR == '4.3': |
1016 | - warn('''PyQt4 version 4.3 detected. |
1017 | -If you experience repeated threading warnings, please update PyQt4. |
1018 | -''') |
1019 | - |
1020 | - self.exec_ = hijack_qt4() |
1021 | - |
1022 | - # Allows us to use both Tk and QT. |
1023 | - self.tk = get_tk() |
1024 | - |
1025 | - self.IP = make_IPython(argv, |
1026 | - user_ns=user_ns, |
1027 | - user_global_ns=user_global_ns, |
1028 | - debug=debug, |
1029 | - shell_class=shell_class, |
1030 | - on_kill=[QtGui.qApp.exit]) |
1031 | - |
1032 | - # HACK: slot for banner in self; it will be passed to the mainloop |
1033 | - # method only and .run() needs it. The actual value will be set by |
1034 | - # .mainloop(). |
1035 | - self._banner = None |
1036 | - |
1037 | - threading.Thread.__init__(self) |
1038 | - |
1039 | - def mainloop(self, sys_exit=0, banner=None): |
1040 | - |
1041 | - from PyQt4 import QtCore, QtGui |
1042 | - |
1043 | - self._banner = banner |
1044 | - |
1045 | - if QtGui.QApplication.startingUp(): |
1046 | - a = QtGui.QApplication(sys.argv) |
1047 | - |
1048 | - self.timer = QtCore.QTimer() |
1049 | - QtCore.QObject.connect(self.timer, |
1050 | - QtCore.SIGNAL('timeout()'), |
1051 | - self.on_timer) |
1052 | - |
1053 | - self.start() |
1054 | - self.timer.start(self.TIMEOUT) |
1055 | - while True: |
1056 | - if self.IP._kill: break |
1057 | - self.exec_() |
1058 | - self.join() |
1059 | - |
1060 | - def on_timer(self): |
1061 | - update_tk(self.tk) |
1062 | - result = self.IP.runcode() |
1063 | - self.timer.start(self.TIMEOUT) |
1064 | - return result |
1065 | - |
1066 | - |
1067 | -# A set of matplotlib public IPython shell classes, for single-threaded (Tk* |
1068 | -# and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use. |
1069 | -def _load_pylab(user_ns): |
1070 | - """Allow users to disable pulling all of pylab into the top-level |
1071 | - namespace. |
1072 | - |
1073 | - This little utility must be called AFTER the actual ipython instance is |
1074 | - running, since only then will the options file have been fully parsed.""" |
1075 | - |
1076 | - ip = ipapi.get() |
1077 | - if ip.options.pylab_import_all: |
1078 | - ip.ex("from matplotlib.pylab import *") |
1079 | - ip.IP.user_config_ns.update(ip.user_ns) |
1080 | - |
1081 | - |
1082 | -class IPShellMatplotlib(IPShell): |
1083 | - """Subclass IPShell with MatplotlibShell as the internal shell. |
1084 | - |
1085 | - Single-threaded class, meant for the Tk* and FLTK* backends. |
1086 | - |
1087 | - Having this on a separate class simplifies the external driver code.""" |
1088 | - |
1089 | - def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
1090 | - IPShell.__init__(self,argv,user_ns,user_global_ns,debug, |
1091 | - shell_class=MatplotlibShell) |
1092 | - _load_pylab(self.IP.user_ns) |
1093 | - |
1094 | -class IPShellMatplotlibGTK(IPShellGTK): |
1095 | - """Subclass IPShellGTK with MatplotlibMTShell as the internal shell. |
1096 | - |
1097 | - Multi-threaded class, meant for the GTK* backends.""" |
1098 | - |
1099 | - def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
1100 | - IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug, |
1101 | - shell_class=MatplotlibMTShell) |
1102 | - _load_pylab(self.IP.user_ns) |
1103 | - |
1104 | -class IPShellMatplotlibWX(IPShellWX): |
1105 | - """Subclass IPShellWX with MatplotlibMTShell as the internal shell. |
1106 | - |
1107 | - Multi-threaded class, meant for the WX* backends.""" |
1108 | - |
1109 | - def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
1110 | - IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug, |
1111 | - shell_class=MatplotlibMTShell) |
1112 | - _load_pylab(self.IP.user_ns) |
1113 | - |
1114 | -class IPShellMatplotlibQt(IPShellQt): |
1115 | - """Subclass IPShellQt with MatplotlibMTShell as the internal shell. |
1116 | - |
1117 | - Multi-threaded class, meant for the Qt* backends.""" |
1118 | - |
1119 | - def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
1120 | - IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug, |
1121 | - shell_class=MatplotlibMTShell) |
1122 | - _load_pylab(self.IP.user_ns) |
1123 | - |
1124 | -class IPShellMatplotlibQt4(IPShellQt4): |
1125 | - """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell. |
1126 | - |
1127 | - Multi-threaded class, meant for the Qt4* backends.""" |
1128 | - |
1129 | - def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1): |
1130 | - IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug, |
1131 | - shell_class=MatplotlibMTShell) |
1132 | - _load_pylab(self.IP.user_ns) |
1133 | - |
1134 | -#----------------------------------------------------------------------------- |
1135 | -# Factory functions to actually start the proper thread-aware shell |
1136 | - |
1137 | -def _select_shell(argv): |
1138 | - """Select a shell from the given argv vector. |
1139 | - |
1140 | - This function implements the threading selection policy, allowing runtime |
1141 | - control of the threading mode, both for general users and for matplotlib. |
1142 | - |
1143 | - Return: |
1144 | - Shell class to be instantiated for runtime operation. |
1145 | - """ |
1146 | - |
1147 | - global USE_TK |
1148 | - |
1149 | - mpl_shell = {'gthread' : IPShellMatplotlibGTK, |
1150 | - 'wthread' : IPShellMatplotlibWX, |
1151 | - 'qthread' : IPShellMatplotlibQt, |
1152 | - 'q4thread' : IPShellMatplotlibQt4, |
1153 | - 'tkthread' : IPShellMatplotlib, # Tk is built-in |
1154 | - } |
1155 | - |
1156 | - th_shell = {'gthread' : IPShellGTK, |
1157 | - 'wthread' : IPShellWX, |
1158 | - 'qthread' : IPShellQt, |
1159 | - 'q4thread' : IPShellQt4, |
1160 | - 'tkthread' : IPShell, # Tk is built-in |
1161 | - } |
1162 | - |
1163 | - backends = {'gthread' : 'GTKAgg', |
1164 | - 'wthread' : 'WXAgg', |
1165 | - 'qthread' : 'QtAgg', |
1166 | - 'q4thread' :'Qt4Agg', |
1167 | - 'tkthread' :'TkAgg', |
1168 | - } |
1169 | - |
1170 | - all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread', |
1171 | - 'tkthread']) |
1172 | - user_opts = set([s.replace('-','') for s in argv[:3]]) |
1173 | - special_opts = user_opts & all_opts |
1174 | - |
1175 | - if 'tk' in special_opts: |
1176 | - USE_TK = True |
1177 | - special_opts.remove('tk') |
1178 | - |
1179 | - if 'pylab' in special_opts: |
1180 | - |
1181 | - try: |
1182 | - import matplotlib |
1183 | - except ImportError: |
1184 | - error('matplotlib could NOT be imported! Starting normal IPython.') |
1185 | - return IPShell |
1186 | - |
1187 | - special_opts.remove('pylab') |
1188 | - # If there's any option left, it means the user wants to force the |
1189 | - # threading backend, else it's auto-selected from the rc file |
1190 | - if special_opts: |
1191 | - th_mode = special_opts.pop() |
1192 | - matplotlib.rcParams['backend'] = backends[th_mode] |
1193 | - else: |
1194 | - backend = matplotlib.rcParams['backend'] |
1195 | - if backend.startswith('GTK'): |
1196 | - th_mode = 'gthread' |
1197 | - elif backend.startswith('WX'): |
1198 | - th_mode = 'wthread' |
1199 | - elif backend.startswith('Qt4'): |
1200 | - th_mode = 'q4thread' |
1201 | - elif backend.startswith('Qt'): |
1202 | - th_mode = 'qthread' |
1203 | - else: |
1204 | - # Any other backend, use plain Tk |
1205 | - th_mode = 'tkthread' |
1206 | - |
1207 | - return mpl_shell[th_mode] |
1208 | - else: |
1209 | - # No pylab requested, just plain threads |
1210 | - try: |
1211 | - th_mode = special_opts.pop() |
1212 | - except KeyError: |
1213 | - th_mode = 'tkthread' |
1214 | - return th_shell[th_mode] |
1215 | - |
1216 | - |
1217 | # This is the one which should be called by external code. |
1218 | def start(user_ns = None): |
1219 | - """Return a running shell instance, dealing with threading options. |
1220 | - |
1221 | - This is a factory function which will instantiate the proper IPython shell |
1222 | - based on the user's threading choice. Such a selector is needed because |
1223 | - different GUI toolkits require different thread handling details.""" |
1224 | - |
1225 | - shell = _select_shell(sys.argv) |
1226 | - return shell(user_ns = user_ns) |
1227 | - |
1228 | -# Some aliases for backwards compatibility |
1229 | -IPythonShell = IPShell |
1230 | -IPythonShellEmbed = IPShellEmbed |
1231 | -#************************ End of file <Shell.py> *************************** |
1232 | + """Return a running shell instance of :class:`IPShell`.""" |
1233 | + return IPShell(user_ns = user_ns) |
1234 | + |
1235 | |
1236 | === removed file 'IPython/core/shellglobals.py' |
1237 | --- IPython/core/shellglobals.py 2009-07-02 17:38:01 +0000 |
1238 | +++ IPython/core/shellglobals.py 1970-01-01 00:00:00 +0000 |
1239 | @@ -1,101 +0,0 @@ |
1240 | -"""Some globals used by the main Shell classes. |
1241 | -""" |
1242 | - |
1243 | -#----------------------------------------------------------------------------- |
1244 | -# Module imports |
1245 | -#----------------------------------------------------------------------------- |
1246 | - |
1247 | -# stdlib |
1248 | -import inspect |
1249 | -import thread |
1250 | - |
1251 | -try: |
1252 | - import ctypes |
1253 | - HAS_CTYPES = True |
1254 | -except ImportError: |
1255 | - HAS_CTYPES = False |
1256 | - |
1257 | -# our own |
1258 | -from IPython.utils.genutils import Term,warn,error,flag_calls, ask_yes_no |
1259 | - |
1260 | -#----------------------------------------------------------------------------- |
1261 | -# Globals |
1262 | -#----------------------------------------------------------------------------- |
1263 | -# global flag to pass around information about Ctrl-C without exceptions |
1264 | -KBINT = False |
1265 | - |
1266 | -# global flag to turn on/off Tk support. |
1267 | -USE_TK = False |
1268 | - |
1269 | -# ID for the main thread, used for cross-thread exceptions |
1270 | -MAIN_THREAD_ID = thread.get_ident() |
1271 | - |
1272 | -# Tag when runcode() is active, for exception handling |
1273 | -CODE_RUN = None |
1274 | - |
1275 | -#----------------------------------------------------------------------------- |
1276 | -# This class is trivial now, but I want to have it in to publish a clean |
1277 | -# interface. Later when the internals are reorganized, code that uses this |
1278 | -# shouldn't have to change. |
1279 | - |
1280 | -if HAS_CTYPES: |
1281 | - # Add async exception support. Trick taken from: |
1282 | - # http://sebulba.wikispaces.com/recipe+thread2 |
1283 | - def _async_raise(tid, exctype): |
1284 | - """raises the exception, performs cleanup if needed""" |
1285 | - if not inspect.isclass(exctype): |
1286 | - raise TypeError("Only types can be raised (not instances)") |
1287 | - res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, |
1288 | - ctypes.py_object(exctype)) |
1289 | - if res == 0: |
1290 | - raise ValueError("invalid thread id") |
1291 | - elif res != 1: |
1292 | - # """if it returns a number greater than one, you're in trouble, |
1293 | - # and you should call it again with exc=NULL to revert the effect""" |
1294 | - ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) |
1295 | - raise SystemError("PyThreadState_SetAsyncExc failed") |
1296 | - |
1297 | - def sigint_handler (signum,stack_frame): |
1298 | - """Sigint handler for threaded apps. |
1299 | - |
1300 | - This is a horrible hack to pass information about SIGINT _without_ |
1301 | - using exceptions, since I haven't been able to properly manage |
1302 | - cross-thread exceptions in GTK/WX. In fact, I don't think it can be |
1303 | - done (or at least that's my understanding from a c.l.py thread where |
1304 | - this was discussed).""" |
1305 | - |
1306 | - global KBINT |
1307 | - |
1308 | - if CODE_RUN: |
1309 | - _async_raise(MAIN_THREAD_ID,KeyboardInterrupt) |
1310 | - else: |
1311 | - KBINT = True |
1312 | - print '\nKeyboardInterrupt - Press <Enter> to continue.', |
1313 | - Term.cout.flush() |
1314 | - |
1315 | -else: |
1316 | - def sigint_handler (signum,stack_frame): |
1317 | - """Sigint handler for threaded apps. |
1318 | - |
1319 | - This is a horrible hack to pass information about SIGINT _without_ |
1320 | - using exceptions, since I haven't been able to properly manage |
1321 | - cross-thread exceptions in GTK/WX. In fact, I don't think it can be |
1322 | - done (or at least that's my understanding from a c.l.py thread where |
1323 | - this was discussed).""" |
1324 | - |
1325 | - global KBINT |
1326 | - |
1327 | - print '\nKeyboardInterrupt - Press <Enter> to continue.', |
1328 | - Term.cout.flush() |
1329 | - # Set global flag so that runsource can know that Ctrl-C was hit |
1330 | - KBINT = True |
1331 | - |
1332 | - |
1333 | -def run_in_frontend(src): |
1334 | - """ Check if source snippet can be run in the REPL thread, as opposed to |
1335 | - GUI mainloop (to prevent unnecessary hanging of mainloop). |
1336 | - """ |
1337 | - |
1338 | - if src.startswith('_ip.system(') and not '\n' in src: |
1339 | - return True |
1340 | - return False |
1341 | |
1342 | === modified file 'IPython/core/tests/test_imports.py' |
1343 | --- IPython/core/tests/test_imports.py 2009-07-02 18:39:49 +0000 |
1344 | +++ IPython/core/tests/test_imports.py 2009-07-21 19:28:37 +0000 |
1345 | @@ -61,9 +61,6 @@ |
1346 | def test_import_shell(): |
1347 | from IPython.core import shell |
1348 | |
1349 | -def test_import_shellglobals(): |
1350 | - from IPython.core import shellglobals |
1351 | - |
1352 | def test_import_ultratb(): |
1353 | from IPython.core import ultratb |
1354 | |
1355 | |
1356 | === modified file 'IPython/core/usage.py' |
1357 | --- IPython/core/usage.py 2009-07-02 18:39:49 +0000 |
1358 | +++ IPython/core/usage.py 2009-07-21 20:48:02 +0000 |
1359 | @@ -39,83 +39,6 @@ |
1360 | In the rest of this text, we will refer to this directory as |
1361 | IPYTHONDIR. |
1362 | |
1363 | - |
1364 | -SPECIAL THREADING OPTIONS |
1365 | - The following special options are ONLY valid at the beginning of the |
1366 | - command line, and not later. This is because they control the initial- |
1367 | - ization of ipython itself, before the normal option-handling mechanism |
1368 | - is active. |
1369 | - |
1370 | - -gthread, -qthread, -q4thread, -wthread, -pylab |
1371 | - |
1372 | - Only ONE of these can be given, and it can only be given as the |
1373 | - first option passed to IPython (it will have no effect in any |
1374 | - other position). They provide threading support for the GTK, QT |
1375 | - and WXWidgets toolkits, and for the matplotlib library. |
1376 | - |
1377 | - With any of the first four options, IPython starts running a |
1378 | - separate thread for the graphical toolkit's operation, so that |
1379 | - you can open and control graphical elements from within an |
1380 | - IPython command line, without blocking. All four provide |
1381 | - essentially the same functionality, respectively for GTK, QT3, |
1382 | - QT4 and WXWidgets (via their Python interfaces). |
1383 | - |
1384 | - Note that with -wthread, you can additionally use the -wxversion |
1385 | - option to request a specific version of wx to be used. This |
1386 | - requires that you have the 'wxversion' Python module installed, |
1387 | - which is part of recent wxPython distributions. |
1388 | - |
1389 | - If -pylab is given, IPython loads special support for the mat- |
1390 | - plotlib library (http://matplotlib.sourceforge.net), allowing |
1391 | - interactive usage of any of its backends as defined in the |
1392 | - user's .matplotlibrc file. It automatically activates GTK, QT |
1393 | - or WX threading for IPyhton if the choice of matplotlib backend |
1394 | - requires it. It also modifies the %run command to correctly |
1395 | - execute (without blocking) any matplotlib-based script which |
1396 | - calls show() at the end. |
1397 | - |
1398 | - -tk The -g/q/q4/wthread options, and -pylab (if matplotlib is |
1399 | - configured to use GTK, QT or WX), will normally block Tk |
1400 | - graphical interfaces. This means that when GTK, QT or WX |
1401 | - threading is active, any attempt to open a Tk GUI will result in |
1402 | - a dead window, and possibly cause the Python interpreter to |
1403 | - crash. An extra option, -tk, is available to address this |
1404 | - issue. It can ONLY be given as a SECOND option after any of the |
1405 | - above (-gthread, -qthread, q4thread, -wthread or -pylab). |
1406 | - |
1407 | - If -tk is given, IPython will try to coordinate Tk threading |
1408 | - with GTK, QT or WX. This is however potentially unreliable, and |
1409 | - you will have to test on your platform and Python configuration |
1410 | - to determine whether it works for you. Debian users have |
1411 | - reported success, apparently due to the fact that Debian builds |
1412 | - all of Tcl, Tk, Tkinter and Python with pthreads support. Under |
1413 | - other Linux environments (such as Fedora Core 2/3), this option |
1414 | - has caused random crashes and lockups of the Python interpreter. |
1415 | - Under other operating systems (Mac OSX and Windows), you'll need |
1416 | - to try it to find out, since currently no user reports are |
1417 | - available. |
1418 | - |
1419 | - There is unfortunately no way for IPython to determine at run- |
1420 | - time whether -tk will work reliably or not, so you will need to |
1421 | - do some experiments before relying on it for regular work. |
1422 | - |
1423 | -A WARNING ABOUT SIGNALS AND THREADS |
1424 | - |
1425 | - When any of the thread systems (GTK, QT or WX) are active, either |
1426 | - directly or via -pylab with a threaded backend, it is impossible to |
1427 | - interrupt long-running Python code via Ctrl-C. IPython can not pass |
1428 | - the KeyboardInterrupt exception (or the underlying SIGINT) across |
1429 | - threads, so any long-running process started from IPython will run to |
1430 | - completion, or will have to be killed via an external (OS-based) |
1431 | - mechanism. |
1432 | - |
1433 | - To the best of my knowledge, this limitation is imposed by the Python |
1434 | - interpreter itself, and it comes from the difficulty of writing |
1435 | - portable signal/threaded code. If any user is an expert on this topic |
1436 | - and can suggest a better solution, I would love to hear about it. In |
1437 | - the IPython sources, look at the shell.py module, and in particular at |
1438 | - the runcode() method. |
1439 | - |
1440 | REGULAR OPTIONS |
1441 | After the above threading options have been given, regular options can |
1442 | follow in any order. All options can be abbreviated to their shortest |
1443 | @@ -132,16 +55,6 @@ |
1444 | -h, --help |
1445 | Show summary of options. |
1446 | |
1447 | - -pylab This can only be given as the first option passed to IPython (it |
1448 | - will have no effect in any other position). It adds special sup- |
1449 | - port for the matplotlib library (http://matplotlib.source- |
1450 | - forge.net), allowing interactive usage of any of its backends as |
1451 | - defined in the user's .matplotlibrc file. It automatically |
1452 | - activates GTK or WX threading for IPyhton if the choice of mat- |
1453 | - plotlib backend requires it. It also modifies the @run command |
1454 | - to correctly execute (without blocking) any matplotlib-based |
1455 | - script which calls show() at the end. |
1456 | - |
1457 | -autocall <val> |
1458 | Make IPython automatically call any callable object even if you |
1459 | didn't type explicit parentheses. For example, 'str 43' becomes |
1460 | |
1461 | === modified file 'IPython/lib/__init__.py' |
1462 | --- IPython/lib/__init__.py 2009-07-01 22:27:02 +0000 |
1463 | +++ IPython/lib/__init__.py 2009-07-21 18:20:44 +0000 |
1464 | @@ -0,0 +1,28 @@ |
1465 | +#!/usr/bin/env python |
1466 | +# encoding: utf-8 |
1467 | +""" |
1468 | +Extra capabilities for IPython |
1469 | +""" |
1470 | + |
1471 | +#----------------------------------------------------------------------------- |
1472 | +# Copyright (C) 2008-2009 The IPython Development Team |
1473 | +# |
1474 | +# Distributed under the terms of the BSD License. The full license is in |
1475 | +# the file COPYING, distributed as part of this software. |
1476 | +#----------------------------------------------------------------------------- |
1477 | + |
1478 | +#----------------------------------------------------------------------------- |
1479 | +# Imports |
1480 | +#----------------------------------------------------------------------------- |
1481 | + |
1482 | +from IPython.lib.inputhook import ( |
1483 | + enable_wx, disable_wx, |
1484 | + enable_gtk, disable_gtk, |
1485 | + enable_qt4, disable_qt4, |
1486 | + enable_tk, disable_tk, |
1487 | + set_inputhook, clear_inputhook |
1488 | +) |
1489 | + |
1490 | +#----------------------------------------------------------------------------- |
1491 | +# Code |
1492 | +#----------------------------------------------------------------------------- |
1493 | \ No newline at end of file |
1494 | |
1495 | === added file 'IPython/lib/inputhook.py' |
1496 | --- IPython/lib/inputhook.py 1970-01-01 00:00:00 +0000 |
1497 | +++ IPython/lib/inputhook.py 2009-07-21 18:20:44 +0000 |
1498 | @@ -0,0 +1,172 @@ |
1499 | +#!/usr/bin/env python |
1500 | +# encoding: utf-8 |
1501 | +""" |
1502 | +Inputhook management for GUI event loop integration. |
1503 | +""" |
1504 | + |
1505 | +#----------------------------------------------------------------------------- |
1506 | +# Copyright (C) 2008-2009 The IPython Development Team |
1507 | +# |
1508 | +# Distributed under the terms of the BSD License. The full license is in |
1509 | +# the file COPYING, distributed as part of this software. |
1510 | +#----------------------------------------------------------------------------- |
1511 | + |
1512 | +#----------------------------------------------------------------------------- |
1513 | +# Imports |
1514 | +#----------------------------------------------------------------------------- |
1515 | + |
1516 | +import ctypes |
1517 | + |
1518 | +#----------------------------------------------------------------------------- |
1519 | +# Code |
1520 | +#----------------------------------------------------------------------------- |
1521 | + |
1522 | + |
1523 | +class InputHookManager(object): |
1524 | + """Manage PyOS_InputHook for different GUI toolkits.""" |
1525 | + |
1526 | + def __init__(self): |
1527 | + self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int) |
1528 | + self._reset() |
1529 | + |
1530 | + def _reset(self): |
1531 | + self._callback_pyfunctype = None |
1532 | + self._callback = None |
1533 | + self._installed = False |
1534 | + |
1535 | + def get_pyos_inputhook(self): |
1536 | + """Return the current PyOS_InputHook as a ctypes.c_void_p. |
1537 | + """ |
1538 | + return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook") |
1539 | + |
1540 | + def get_pyos_inputhook_as_func(self): |
1541 | + """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE. |
1542 | + """ |
1543 | + return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook") |
1544 | + |
1545 | + def set_inputhook(self, callback): |
1546 | + """Set PyOS_InputHook to callback and return the previous one. |
1547 | + """ |
1548 | + self._callback = callback |
1549 | + self._callback_pyfunctype = self.PYFUNC(callback) |
1550 | + pyos_inputhook_ptr = self.get_pyos_inputhook() |
1551 | + original = self.get_pyos_inputhook_as_func() |
1552 | + pyos_inputhook_ptr.value = \ |
1553 | + ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value |
1554 | + self._installed = True |
1555 | + return original |
1556 | + |
1557 | + def clear_inputhook(self): |
1558 | + """Set PyOS_InputHook to NULL and return the previous one. |
1559 | + """ |
1560 | + pyos_inputhook_ptr = self.get_pyos_inputhook() |
1561 | + original = self.get_pyos_inputhook_as_func() |
1562 | + pyos_inputhook_ptr.value = ctypes.c_void_p(None).value |
1563 | + self._reset() |
1564 | + return original |
1565 | + |
1566 | + def enable_wx(self): |
1567 | + """Enable event loop integration with wxPython. |
1568 | + |
1569 | + This methods sets the PyOS_InputHook for wxPython, which allows |
1570 | + the wxPython to integrate with terminal based applications like |
1571 | + IPython. |
1572 | + |
1573 | + Once this has been called, you can use wx interactively by doing:: |
1574 | + |
1575 | + >>> import wx |
1576 | + >>> app = wx.App(redirect=False, clearSigInt=False) |
1577 | + |
1578 | + Both options this constructor are important for things to work |
1579 | + properly in an interactive context. |
1580 | + |
1581 | + But, *don't start the event loop*. That is handled automatically by |
1582 | + PyOS_InputHook. |
1583 | + """ |
1584 | + from IPython.lib.inputhookwx import inputhook_wx |
1585 | + self.set_inputhook(inputhook_wx) |
1586 | + |
1587 | + def disable_wx(self): |
1588 | + """Disable event loop integration with wxPython. |
1589 | + |
1590 | + This merely sets PyOS_InputHook to NULL. |
1591 | + """ |
1592 | + self.clear_inputhook() |
1593 | + |
1594 | + def enable_qt4(self): |
1595 | + """Enable event loop integration with PyQt4. |
1596 | + |
1597 | + This methods sets the PyOS_InputHook for wxPython, which allows |
1598 | + the PyQt4 to integrate with terminal based applications like |
1599 | + IPython. |
1600 | + |
1601 | + Once this has been called, you can simply create a QApplication and |
1602 | + use it. But, *don't start the event loop*. That is handled |
1603 | + automatically by PyOS_InputHook. |
1604 | + """ |
1605 | + from PyQt4 import QtCore |
1606 | + # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook |
1607 | + # was set when QtCore was imported, but if it ever got removed, |
1608 | + # you couldn't reset it. For earlier versions we can |
1609 | + # probably implement a ctypes version. |
1610 | + try: |
1611 | + QtCore.pyqtRestoreInputHook() |
1612 | + except AttributeError: |
1613 | + pass |
1614 | + |
1615 | + def disable_qt4(self): |
1616 | + """Disable event loop integration with PyQt4. |
1617 | + |
1618 | + This merely sets PyOS_InputHook to NULL. |
1619 | + """ |
1620 | + self.clear_inputhook() |
1621 | + |
1622 | + def enable_gtk(self): |
1623 | + """Enable event loop integration with PyGTK. |
1624 | + |
1625 | + This methods sets the PyOS_InputHook for PyGTK, which allows |
1626 | + the PyGTK to integrate with terminal based applications like |
1627 | + IPython. |
1628 | + |
1629 | + Once this has been called, you can simple create PyGTK objects and |
1630 | + use them. But, *don't start the event loop*. That is handled |
1631 | + automatically by PyOS_InputHook. |
1632 | + """ |
1633 | + import gtk |
1634 | + try: |
1635 | + gtk.set_interactive(True) |
1636 | + except AttributeError: |
1637 | + # For older versions of gtk, use our own ctypes version |
1638 | + from IPython.lib.inputhookgtk import inputhook_gtk |
1639 | + add_inputhook(inputhook_gtk) |
1640 | + |
1641 | + def disable_gtk(self): |
1642 | + """Disable event loop integration with PyGTK. |
1643 | + |
1644 | + This merely sets PyOS_InputHook to NULL. |
1645 | + """ |
1646 | + self.clear_inputhook() |
1647 | + |
1648 | + def enable_tk(self): |
1649 | + # Creating a Tkinter.Tk object sets PyOS_InputHook() |
1650 | + pass |
1651 | + |
1652 | + def disable_tk(self): |
1653 | + """Disable event loop integration with Tkinter. |
1654 | + |
1655 | + This merely sets PyOS_InputHook to NULL. |
1656 | + """ |
1657 | + self.clear_inputhook() |
1658 | + |
1659 | +inputhook_manager = InputHookManager() |
1660 | + |
1661 | +enable_wx = inputhook_manager.enable_wx |
1662 | +disable_wx = inputhook_manager.disable_wx |
1663 | +enable_qt4 = inputhook_manager.enable_qt4 |
1664 | +disable_qt4 = inputhook_manager.disable_qt4 |
1665 | +enable_gtk = inputhook_manager.enable_gtk |
1666 | +disable_gtk = inputhook_manager.disable_gtk |
1667 | +enable_tk = inputhook_manager.enable_tk |
1668 | +disable_tk = inputhook_manager.disable_tk |
1669 | +clear_inputhook = inputhook_manager.clear_inputhook |
1670 | +set_inputhook = inputhook_manager.set_inputhook |
1671 | \ No newline at end of file |
1672 | |
1673 | === added file 'IPython/lib/inputhookgtk.py' |
1674 | --- IPython/lib/inputhookgtk.py 1970-01-01 00:00:00 +0000 |
1675 | +++ IPython/lib/inputhookgtk.py 2009-07-21 18:20:44 +0000 |
1676 | @@ -0,0 +1,36 @@ |
1677 | +#!/usr/bin/env python |
1678 | +# encoding: utf-8 |
1679 | +""" |
1680 | +Enable pygtk to be used interacive by setting PyOS_InputHook. |
1681 | + |
1682 | +Authors: Brian Granger |
1683 | +""" |
1684 | + |
1685 | +#----------------------------------------------------------------------------- |
1686 | +# Copyright (C) 2008-2009 The IPython Development Team |
1687 | +# |
1688 | +# Distributed under the terms of the BSD License. The full license is in |
1689 | +# the file COPYING, distributed as part of this software. |
1690 | +#----------------------------------------------------------------------------- |
1691 | + |
1692 | +#----------------------------------------------------------------------------- |
1693 | +# Imports |
1694 | +#----------------------------------------------------------------------------- |
1695 | + |
1696 | +import sys |
1697 | +import gtk, gobject |
1698 | + |
1699 | +#----------------------------------------------------------------------------- |
1700 | +# Code |
1701 | +#----------------------------------------------------------------------------- |
1702 | + |
1703 | + |
1704 | +def _main_quit(*args, **kwargs): |
1705 | + gtk.main_quit() |
1706 | + return False |
1707 | + |
1708 | +def inputhook_gtk(): |
1709 | + gobject.io_add_watch(sys.stdin, gobject.IO_IN, _main_quit) |
1710 | + gtk.main() |
1711 | + return 0 |
1712 | + |
1713 | |
1714 | === added file 'IPython/lib/inputhookwx.py' |
1715 | --- IPython/lib/inputhookwx.py 1970-01-01 00:00:00 +0000 |
1716 | +++ IPython/lib/inputhookwx.py 2009-07-21 17:42:04 +0000 |
1717 | @@ -0,0 +1,153 @@ |
1718 | +#!/usr/bin/env python |
1719 | +# encoding: utf-8 |
1720 | + |
1721 | +""" |
1722 | +Enable wxPython to be used interacive by setting PyOS_InputHook. |
1723 | + |
1724 | +Authors: Robin Dunn, Brian Granger, Ondrej Certik |
1725 | +""" |
1726 | + |
1727 | +#----------------------------------------------------------------------------- |
1728 | +# Copyright (C) 2008-2009 The IPython Development Team |
1729 | +# |
1730 | +# Distributed under the terms of the BSD License. The full license is in |
1731 | +# the file COPYING, distributed as part of this software. |
1732 | +#----------------------------------------------------------------------------- |
1733 | + |
1734 | +#----------------------------------------------------------------------------- |
1735 | +# Imports |
1736 | +#----------------------------------------------------------------------------- |
1737 | + |
1738 | +import os |
1739 | +import sys |
1740 | +import time |
1741 | +from timeit import default_timer as clock |
1742 | +import wx |
1743 | + |
1744 | +if os.name == 'posix': |
1745 | + import select |
1746 | +elif sys.platform == 'win32': |
1747 | + import msvcrt |
1748 | + |
1749 | +#----------------------------------------------------------------------------- |
1750 | +# Code |
1751 | +#----------------------------------------------------------------------------- |
1752 | + |
1753 | +def stdin_ready(): |
1754 | + if os.name == 'posix': |
1755 | + infds, outfds, erfds = select.select([sys.stdin],[],[],0) |
1756 | + if infds: |
1757 | + return True |
1758 | + else: |
1759 | + return False |
1760 | + elif sys.platform == 'win32': |
1761 | + return msvcrt.kbhit() |
1762 | + |
1763 | + |
1764 | +def inputhook_wx1(): |
1765 | + """Run the wx event loop by processing pending events only. |
1766 | + |
1767 | + This approach seems to work, but its performance is not great as it |
1768 | + relies on having PyOS_InputHook called regularly. |
1769 | + """ |
1770 | + app = wx.GetApp() |
1771 | + if app is not None: |
1772 | + assert wx.Thread_IsMain() |
1773 | + |
1774 | + # Make a temporary event loop and process system events until |
1775 | + # there are no more waiting, then allow idle events (which |
1776 | + # will also deal with pending or posted wx events.) |
1777 | + evtloop = wx.EventLoop() |
1778 | + ea = wx.EventLoopActivator(evtloop) |
1779 | + while evtloop.Pending(): |
1780 | + evtloop.Dispatch() |
1781 | + app.ProcessIdle() |
1782 | + del ea |
1783 | + return 0 |
1784 | + |
1785 | +class EventLoopTimer(wx.Timer): |
1786 | + |
1787 | + def __init__(self, func): |
1788 | + self.func = func |
1789 | + wx.Timer.__init__(self) |
1790 | + |
1791 | + def Notify(self): |
1792 | + self.func() |
1793 | + |
1794 | +class EventLoopRunner(object): |
1795 | + |
1796 | + def Run(self, time): |
1797 | + self.evtloop = wx.EventLoop() |
1798 | + self.timer = EventLoopTimer(self.check_stdin) |
1799 | + self.timer.Start(time) |
1800 | + self.evtloop.Run() |
1801 | + |
1802 | + def check_stdin(self): |
1803 | + if stdin_ready(): |
1804 | + self.timer.Stop() |
1805 | + self.evtloop.Exit() |
1806 | + |
1807 | +def inputhook_wx2(): |
1808 | + """Run the wx event loop, polling for stdin. |
1809 | + |
1810 | + This version runs the wx eventloop for an undetermined amount of time, |
1811 | + during which it periodically checks to see if anything is ready on |
1812 | + stdin. If anything is ready on stdin, the event loop exits. |
1813 | + |
1814 | + The argument to elr.Run controls how often the event loop looks at stdin. |
1815 | + This determines the responsiveness at the keyboard. A setting of 1000 |
1816 | + enables a user to type at most 1 char per second. I have found that a |
1817 | + setting of 10 gives good keyboard response. We can shorten it further, |
1818 | + but eventually performance would suffer from calling select/kbhit too |
1819 | + often. |
1820 | + """ |
1821 | + app = wx.GetApp() |
1822 | + if app is not None: |
1823 | + assert wx.Thread_IsMain() |
1824 | + elr = EventLoopRunner() |
1825 | + # As this time is made shorter, keyboard response improves, but idle |
1826 | + # CPU load goes up. 10 ms seems like a good compromise. |
1827 | + elr.Run(time=10) # CHANGE time here to control polling interval |
1828 | + return 0 |
1829 | + |
1830 | +def inputhook_wx3(): |
1831 | + """Run the wx event loop by processing pending events only. |
1832 | + |
1833 | + This is like inputhook_wx1, but it keeps processing pending events |
1834 | + until stdin is ready. After processing all pending events, a call to |
1835 | + time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%. |
1836 | + This sleep time should be tuned though for best performance. |
1837 | + """ |
1838 | + app = wx.GetApp() |
1839 | + if app is not None: |
1840 | + assert wx.Thread_IsMain() |
1841 | + |
1842 | + evtloop = wx.EventLoop() |
1843 | + ea = wx.EventLoopActivator(evtloop) |
1844 | + t = clock() |
1845 | + while not stdin_ready(): |
1846 | + while evtloop.Pending(): |
1847 | + t = clock() |
1848 | + evtloop.Dispatch() |
1849 | + app.ProcessIdle() |
1850 | + # We need to sleep at this point to keep the idle CPU load |
1851 | + # low. However, if sleep to long, GUI response is poor. As |
1852 | + # a compromise, we watch how often GUI events are being processed |
1853 | + # and switch between a short and long sleep time. Here are some |
1854 | + # stats useful in helping to tune this. |
1855 | + # time CPU load |
1856 | + # 0.001 13% |
1857 | + # 0.005 3% |
1858 | + # 0.01 1.5% |
1859 | + # 0.05 0.5% |
1860 | + if clock()-t > 0.1: |
1861 | + # Few GUI events coming in, so we can sleep longer |
1862 | + time.sleep(0.05) |
1863 | + else: |
1864 | + # Many GUI events coming in, so sleep only very little |
1865 | + time.sleep(0.001) |
1866 | + del ea |
1867 | + return 0 |
1868 | + |
1869 | +# This is our default implementation |
1870 | +inputhook_wx = inputhook_wx3 |
1871 | \ No newline at end of file |
1872 | |
1873 | === modified file 'IPython/testing/iptest.py' |
1874 | --- IPython/testing/iptest.py 2009-07-27 22:38:27 +0000 |
1875 | +++ IPython/testing/iptest.py 2009-07-27 22:52:00 +0000 |
1876 | @@ -55,6 +55,8 @@ |
1877 | have_foolscap = test_for('foolscap') |
1878 | have_objc = test_for('objc') |
1879 | have_pexpect = test_for('pexpect') |
1880 | +have_gtk = test_for('gtk') |
1881 | +have_gobject = test_for('gobject') |
1882 | |
1883 | |
1884 | def make_exclude(): |
1885 | @@ -73,13 +75,18 @@ |
1886 | pjoin('IPython', 'extensions', 'numeric_formats'), |
1887 | pjoin('IPython', 'testing', 'attic'), |
1888 | pjoin('IPython', 'testing', 'tools'), |
1889 | - pjoin('IPython', 'testing', 'mkdoctests') |
1890 | + pjoin('IPython', 'testing', 'mkdoctests'), |
1891 | + pjoin('IPython', 'lib', 'inputhook') |
1892 | ] |
1893 | |
1894 | if not have_wx: |
1895 | EXCLUDE.append(pjoin('IPython', 'extensions', 'igrid')) |
1896 | EXCLUDE.append(pjoin('IPython', 'gui')) |
1897 | EXCLUDE.append(pjoin('IPython', 'frontend', 'wx')) |
1898 | + EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookwx')) |
1899 | + |
1900 | + if not have_gtk or not have_gobject: |
1901 | + EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookgtk')) |
1902 | |
1903 | if not have_objc: |
1904 | EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa')) |
1905 | |
1906 | === modified file 'docs/examples/core/example-embed.py' |
1907 | --- docs/examples/core/example-embed.py 2008-06-09 22:51:37 +0000 |
1908 | +++ docs/examples/core/example-embed.py 2009-07-21 19:34:22 +0000 |
1909 | @@ -31,7 +31,7 @@ |
1910 | '-po','Out<\\#>: ','-nosep'] |
1911 | |
1912 | # First import the embeddable shell class |
1913 | -from IPython.Shell import IPShellEmbed |
1914 | +from IPython.core.shell import IPShellEmbed |
1915 | |
1916 | # Now create an instance of the embeddable shell. The first argument is a |
1917 | # string with options exactly as you would type them if you were starting |
1918 | |
1919 | === modified file 'docs/man/ipython.1' |
1920 | --- docs/man/ipython.1 2008-06-09 22:47:41 +0000 |
1921 | +++ docs/man/ipython.1 2009-07-21 20:48:02 +0000 |
1922 | @@ -31,62 +31,6 @@ |
1923 | dynamic object introspection, easier configuration, command |
1924 | completion, access to the system shell, integration with numerical and |
1925 | scientific computing tools, and more. |
1926 | -.SH SPECIAL THREADING OPTIONS |
1927 | -The following special options are ONLY valid at the beginning of the command |
1928 | -line, and not later. This is because they control the initialization of |
1929 | -ipython itself, before the normal option-handling mechanism is active. |
1930 | -.TP |
1931 | -.B \-gthread, \-qthread, \-q4thread, \-wthread, \-pylab |
1932 | -Only ONE of these can be given, and it can only be given as the first option |
1933 | -passed to IPython (it will have no effect in any other position). They provide |
1934 | -threading support for the GTK, QT3, QT4 and WXWidgets toolkits, for the |
1935 | -matplotlib library and Twisted reactor. |
1936 | -.br |
1937 | -.sp 1 |
1938 | -With any of the first four options, IPython starts running a separate thread |
1939 | -for the graphical toolkit's operation, so that you can open and control |
1940 | -graphical elements from within an IPython command line, without blocking. All |
1941 | -four provide essentially the same functionality, respectively for GTK, QT3, QT4 |
1942 | -and WXWidgets (via their Python interfaces). |
1943 | -.br |
1944 | -.sp 1 |
1945 | -Note that with \-wthread, you can additionally use the \-wxversion option to |
1946 | -request a specific version of wx to be used. This requires that you have the |
1947 | -wxversion Python module installed, which is part of recent wxPython |
1948 | -distributions. |
1949 | -.br |
1950 | -.sp 1 |
1951 | -If \-pylab is given, IPython loads special support for the matplotlib library |
1952 | -(http://matplotlib.sourceforge.net), allowing interactive usage of any of its |
1953 | -backends as defined in the user's .matplotlibrc file. It automatically |
1954 | -activates GTK, QT or WX threading for IPyhton if the choice of matplotlib |
1955 | -backend requires it. It also modifies the %run command to correctly execute |
1956 | -(without blocking) any matplotlib-based script which calls show() at the end. |
1957 | -.TP |
1958 | -.B \-tk |
1959 | -The \-g/q/q4/wthread options, and \-pylab (if matplotlib is configured to use |
1960 | -GTK, QT or WX), will normally block Tk graphical interfaces. This means that |
1961 | -when GTK, QT or WX threading is active, any attempt to open a Tk GUI will |
1962 | -result in a dead window, and possibly cause the Python interpreter to crash. |
1963 | -An extra option, \-tk, is available to address this issue. It can ONLY be |
1964 | -given as a SECOND option after any of the above (\-gthread, \-qthread, |
1965 | -\-wthread or \-pylab). |
1966 | -.br |
1967 | -.sp 1 |
1968 | -If \-tk is given, IPython will try to coordinate Tk threading with GTK, QT or |
1969 | -WX. This is however potentially unreliable, and you will have to test on your |
1970 | -platform and Python configuration to determine whether it works for you. |
1971 | -Debian users have reported success, apparently due to the fact that Debian |
1972 | -builds all of Tcl, Tk, Tkinter and Python with pthreads support. Under other |
1973 | -Linux environments (such as Fedora Core 2), this option has caused random |
1974 | -crashes and lockups of the Python interpreter. Under other operating systems |
1975 | -(Mac OSX and Windows), you'll need to try it to find out, since currently no |
1976 | -user reports are available. |
1977 | -.br |
1978 | -.sp 1 |
1979 | -There is unfortunately no way for IPython to determine at runtime whether \-tk |
1980 | -will work reliably or not, so you will need to do some experiments before |
1981 | -relying on it for regular work. |
1982 | . |
1983 | .SH REGULAR OPTIONS |
1984 | After the above threading options have been given, regular options can follow |
1985 | |
1986 | === modified file 'scripts/ipython_win_post_install.py' |
1987 | --- scripts/ipython_win_post_install.py 2009-07-02 22:59:26 +0000 |
1988 | +++ scripts/ipython_win_post_install.py 2009-07-21 20:48:02 +0000 |
1989 | @@ -62,12 +62,8 @@ |
1990 | cmd = '"%s" -p sh' % ipybase |
1991 | mkshortcut(python,'IPython (command prompt mode)',link,cmd) |
1992 | |
1993 | - link = pjoin(ip_start_menu, 'pylab.lnk') |
1994 | - cmd = '"%s" -pylab' % ipybase |
1995 | - mkshortcut(python,'IPython (PyLab mode)',link,cmd) |
1996 | - |
1997 | link = pjoin(ip_start_menu, 'scipy.lnk') |
1998 | - cmd = '"%s" -pylab -p scipy' % ipybase |
1999 | + cmd = '"%s" -p scipy' % ipybase |
2000 | mkshortcut(python,'IPython (scipy profile)',link,cmd) |
2001 | |
2002 | link = pjoin(ip_start_menu, 'IPython test suite.lnk') |
2003 | |
2004 | === removed file 'test/test_shell_options.py' |
2005 | --- test/test_shell_options.py 2008-03-10 05:00:33 +0000 |
2006 | +++ test/test_shell_options.py 1970-01-01 00:00:00 +0000 |
2007 | @@ -1,94 +0,0 @@ |
2008 | -#!/usr/bin/env python |
2009 | -"""A few unit tests for the Shell module. |
2010 | -""" |
2011 | - |
2012 | -from unittest import TestCase, main |
2013 | - |
2014 | -from IPython import Shell |
2015 | - |
2016 | -try: |
2017 | - import matplotlib |
2018 | - has_matplotlib = True |
2019 | -except ImportError: |
2020 | - has_matplotlib = False |
2021 | - |
2022 | -class ShellTestBase(TestCase): |
2023 | - def _test(self,argv,ans): |
2024 | - shell = Shell._select_shell(argv) |
2025 | - err = 'Got %s != %s' % (shell,ans) |
2026 | - self.failUnlessEqual(shell,ans,err) |
2027 | - |
2028 | -class ArgsTestCase(ShellTestBase): |
2029 | - def test_plain(self): |
2030 | - self._test([],Shell.IPShell) |
2031 | - |
2032 | - def test_tkthread(self): |
2033 | - self._test(['-tkthread'],Shell.IPShell) |
2034 | - |
2035 | - def test_gthread(self): |
2036 | - self._test(['-gthread'],Shell.IPShellGTK) |
2037 | - |
2038 | - def test_qthread(self): |
2039 | - self._test(['-qthread'],Shell.IPShellQt) |
2040 | - |
2041 | - def test_q4thread(self): |
2042 | - self._test(['-q4thread'],Shell.IPShellQt4) |
2043 | - |
2044 | - def test_wthread(self): |
2045 | - self._test(['-wthread'],Shell.IPShellWX) |
2046 | - |
2047 | -if has_matplotlib: |
2048 | - class MplArgsTestCase(ShellTestBase): |
2049 | - def setUp(self): |
2050 | - self.backend = matplotlib.rcParams['backend'] |
2051 | - |
2052 | - def tearDown(self): |
2053 | - matplotlib.rcParams['backend'] = self.backend |
2054 | - |
2055 | - def _test(self,argv,ans): |
2056 | - shell = Shell._select_shell(argv) |
2057 | - err = 'Got %s != %s' % (shell,ans) |
2058 | - self.failUnlessEqual(shell,ans,err) |
2059 | - |
2060 | - def test_tk(self): |
2061 | - matplotlib.rcParams['backend'] = 'TkAgg' |
2062 | - self._test(['-pylab'],Shell.IPShellMatplotlib) |
2063 | - |
2064 | - def test_ps(self): |
2065 | - matplotlib.rcParams['backend'] = 'PS' |
2066 | - self._test(['-pylab'],Shell.IPShellMatplotlib) |
2067 | - |
2068 | - def test_gtk(self): |
2069 | - matplotlib.rcParams['backend'] = 'GTKAgg' |
2070 | - self._test(['-pylab'],Shell.IPShellMatplotlibGTK) |
2071 | - |
2072 | - def test_gtk_2(self): |
2073 | - self._test(['-gthread','-pylab'],Shell.IPShellMatplotlibGTK) |
2074 | - self.failUnlessEqual(matplotlib.rcParams['backend'],'GTKAgg') |
2075 | - |
2076 | - def test_qt(self): |
2077 | - matplotlib.rcParams['backend'] = 'QtAgg' |
2078 | - self._test(['-pylab'],Shell.IPShellMatplotlibQt) |
2079 | - |
2080 | - def test_qt_2(self): |
2081 | - self._test(['-qthread','-pylab'],Shell.IPShellMatplotlibQt) |
2082 | - self.failUnlessEqual(matplotlib.rcParams['backend'],'QtAgg') |
2083 | - |
2084 | - def test_qt4(self): |
2085 | - matplotlib.rcParams['backend'] = 'Qt4Agg' |
2086 | - self._test(['-pylab'],Shell.IPShellMatplotlibQt4) |
2087 | - |
2088 | - def test_qt4_2(self): |
2089 | - self._test(['-q4thread','-pylab'],Shell.IPShellMatplotlibQt4) |
2090 | - self.failUnlessEqual(matplotlib.rcParams['backend'],'Qt4Agg') |
2091 | - |
2092 | - def test_wx(self): |
2093 | - matplotlib.rcParams['backend'] = 'WxAgg' |
2094 | - self._test(['-pylab'],Shell.IPShellMatplotlibWX) |
2095 | - |
2096 | - def test_wx_2(self): |
2097 | - self._test(['-pylab','-wthread'],Shell.IPShellMatplotlibWX) |
2098 | - self.failUnlessEqual(matplotlib.rcParams['backend'],'WXAgg') |
2099 | - |
2100 | - |
2101 | -main() |
New features
------------
This branch contains a radical refactor of how IPython integrates with GUI event loop. While it has been tested extensively, it will require a bit of polishing, but we want to get this into trunk ASAP so people can begin to play with it at SciPy.
* Added a new module :mod:`IPython. lib.inputhook` to manage the integration
with GUI event loops using `PyOS_InputHook`. See the docstrings in this
module for details.
Bug fixes
---------
Backwards incompatible changes ------- ------- ------- --
-------
* Removed :mod:`shellglobals` as it was obsolete. core.shell` . These are no `IPython. lib.inputhook` .
* Removed all the threaded shells in :mod:`IPython.
longer needed because of the new capabilities in
:mod:
* Should we remove the old threading flags like `-pylab`, `-qthread`, etc.?