Merge lp://qastaging/~ipython-dev/ipython/inputhook into lp://qastaging/ipython/0.11

Proposed by Brian Granger
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
Reviewer Review Type Date Requested Status
Fernando Perez Needs Information
Review via email: mp+10127@code.qastaging.launchpad.net
To post a comment you must log in.
Revision history for this message
Brian Granger (ellisonbg) wrote :

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.
* Removed all the threaded shells in :mod:`IPython.core.shell`. These are no
  longer needed because of the new capabilities in
  :mod:`IPython.lib.inputhook`.
* Should we remove the old threading flags like `-pylab`, `-qthread`, etc.?

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.

Revision history for this message
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.lib.inputhook` to manage the integration
  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.use('wxagg')
    from matplotlib import pyplot
    pyplot.interactive(True)
    # 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.core.shell`. These are no
  longer needed because of the new capabilities in
  :mod:`IPython.lib.inputhook`.
* The old threading command line flags (pylab/wthread/etc.) have been
  deprecated. Use :mod:`IPython.inputhook` or the new :command:`%gui` magic
  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.

Revision history for this message
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/usr/lib/python2.6/site-packages/IPython/lib/inputhook.pyc in enable_gtk(self, app)
    177 # For older versions of gtk, use our own ctypes version

    178 from IPython.lib.inputhookgtk import inputhook_gtk
--> 179 add_inputhook(inputhook_gtk)
    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]
        ip.magic('gui -a %s' % guitk)
    except IndexError:
        guitk = ''
    backends = dict(wx='wxagg', qt4='qt4agg', gtk='gtkagg', tk='tkagg')
    backends[''] = 'tkagg'
    matplotlib.use(backends[guitk])
    matplotlib.interactive(True)

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.stdout.flush()
    time.sleep(1)

review: Needs Information
Revision history for this message
Brian Granger (ellisonbg) wrote :
Download full text (3.6 KiB)

> 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.lib.inputhook import current_gui
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/usr/lib/python2.6/site-packages/IPython/lib/inputhook.pyc in
> enable_gtk(self, app)
> 177 # For older versions of gtk, use our own ctypes version
>
> 178 from IPython.lib.inputhookgtk import inputhook_gtk
> --> 179 add_inputhook(inputhook_gtk)
> 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.use(backends[guitk])
> matplotlib.interactive(True)
>
> 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
> ...

Read more...

Revision history for this message
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://tinyurl.com/vainio

Revision history for this message
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.lib.inputhook import current_gui

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.use(backends[guitk])
matplotlib.interactive(True)

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.stdout.flush()
   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>

Revision history for this message
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...

Revision history for this message
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://code.launchpad.net/~ipython-dev/ipython/inputhook/+merge/10127<https://code.launchpad.net/%7Eipython-dev/ipython/inputhook/+merge/10127>
> 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.

Revision history for this message
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.

Revision history for this message
Fernando Perez (fdo.perez) wrote :
Download full text (4.5 KiB)

- Trivial problem in gtk example:

In [2]: run gui-gtk.py
---------------------------------------------------------------------------
NameError Traceback (most recent call last)

/home/fperez/ipython/repo/inputhook-lp/docs/examples/lib/gui-gtk.py in <module>()
     22 button.connect("clicked", hello_world, None)
     23
---> 24 window.add(self.button)
     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/ipython/repo/inputhook-lp/docs/examples/lib/gui-gtk.py in <module>()
     28 try:
     29 from IPython.lib.inputhook import appstart_gtk
---> 30 appstart_gtk()
     31 except ImportError:
     32 gtk.main()

/home/fperez/usr/lib/python2.6/site-packages/IPython/lib/inputhook.pyc in appstart_gtk()
    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 ...

Read more...

review: Needs Information
Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
Brian Granger (ellisonbg) wrote :
Download full text (4.9 KiB)

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/ipython/repo/inputhook-lp/docs/examples/lib/gui-gtk.py in
> <module>()
> 22 button.connect("clicked", hello_world, None)
> 23
> ---> 24 window.add(self.button)
> 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...

Read more...

Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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()

Subscribers

People subscribed via source and target branches