##// END OF EJS Templates
Remove unused imports in IPython.lib
Thomas Kluyver -
Show More
@@ -1,529 +1,528 b''
1 # coding: utf-8
1 # coding: utf-8
2 """
2 """
3 Inputhook management for GUI event loop integration.
3 Inputhook management for GUI event loop integration.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 try:
17 try:
18 import ctypes
18 import ctypes
19 except ImportError:
19 except ImportError:
20 ctypes = None
20 ctypes = None
21 import os
21 import os
22 import sys
22 import sys
23 from distutils.version import LooseVersion as V
23 from distutils.version import LooseVersion as V
24
24
25 from IPython.utils.warn import warn
25 from IPython.utils.warn import warn
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Constants
28 # Constants
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 # Constants for identifying the GUI toolkits.
31 # Constants for identifying the GUI toolkits.
32 GUI_WX = 'wx'
32 GUI_WX = 'wx'
33 GUI_QT = 'qt'
33 GUI_QT = 'qt'
34 GUI_QT4 = 'qt4'
34 GUI_QT4 = 'qt4'
35 GUI_GTK = 'gtk'
35 GUI_GTK = 'gtk'
36 GUI_TK = 'tk'
36 GUI_TK = 'tk'
37 GUI_OSX = 'osx'
37 GUI_OSX = 'osx'
38 GUI_GLUT = 'glut'
38 GUI_GLUT = 'glut'
39 GUI_PYGLET = 'pyglet'
39 GUI_PYGLET = 'pyglet'
40 GUI_GTK3 = 'gtk3'
40 GUI_GTK3 = 'gtk3'
41 GUI_NONE = 'none' # i.e. disable
41 GUI_NONE = 'none' # i.e. disable
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Utilities
44 # Utilities
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 def _stdin_ready_posix():
47 def _stdin_ready_posix():
48 """Return True if there's something to read on stdin (posix version)."""
48 """Return True if there's something to read on stdin (posix version)."""
49 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
49 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
50 return bool(infds)
50 return bool(infds)
51
51
52 def _stdin_ready_nt():
52 def _stdin_ready_nt():
53 """Return True if there's something to read on stdin (nt version)."""
53 """Return True if there's something to read on stdin (nt version)."""
54 return msvcrt.kbhit()
54 return msvcrt.kbhit()
55
55
56 def _stdin_ready_other():
56 def _stdin_ready_other():
57 """Return True, assuming there's something to read on stdin."""
57 """Return True, assuming there's something to read on stdin."""
58 return True #
58 return True #
59
59
60
60
61 def _ignore_CTRL_C_posix():
61 def _ignore_CTRL_C_posix():
62 """Ignore CTRL+C (SIGINT)."""
62 """Ignore CTRL+C (SIGINT)."""
63 signal.signal(signal.SIGINT, signal.SIG_IGN)
63 signal.signal(signal.SIGINT, signal.SIG_IGN)
64
64
65 def _allow_CTRL_C_posix():
65 def _allow_CTRL_C_posix():
66 """Take CTRL+C into account (SIGINT)."""
66 """Take CTRL+C into account (SIGINT)."""
67 signal.signal(signal.SIGINT, signal.default_int_handler)
67 signal.signal(signal.SIGINT, signal.default_int_handler)
68
68
69 def _ignore_CTRL_C_other():
69 def _ignore_CTRL_C_other():
70 """Ignore CTRL+C (not implemented)."""
70 """Ignore CTRL+C (not implemented)."""
71 pass
71 pass
72
72
73 def _allow_CTRL_C_other():
73 def _allow_CTRL_C_other():
74 """Take CTRL+C into account (not implemented)."""
74 """Take CTRL+C into account (not implemented)."""
75 pass
75 pass
76
76
77 if os.name == 'posix':
77 if os.name == 'posix':
78 import select
78 import select
79 import signal
79 import signal
80 stdin_ready = _stdin_ready_posix
80 stdin_ready = _stdin_ready_posix
81 ignore_CTRL_C = _ignore_CTRL_C_posix
81 ignore_CTRL_C = _ignore_CTRL_C_posix
82 allow_CTRL_C = _allow_CTRL_C_posix
82 allow_CTRL_C = _allow_CTRL_C_posix
83 elif os.name == 'nt':
83 elif os.name == 'nt':
84 import msvcrt
84 import msvcrt
85 stdin_ready = _stdin_ready_nt
85 stdin_ready = _stdin_ready_nt
86 ignore_CTRL_C = _ignore_CTRL_C_other
86 ignore_CTRL_C = _ignore_CTRL_C_other
87 allow_CTRL_C = _allow_CTRL_C_other
87 allow_CTRL_C = _allow_CTRL_C_other
88 else:
88 else:
89 stdin_ready = _stdin_ready_other
89 stdin_ready = _stdin_ready_other
90 ignore_CTRL_C = _ignore_CTRL_C_other
90 ignore_CTRL_C = _ignore_CTRL_C_other
91 allow_CTRL_C = _allow_CTRL_C_other
91 allow_CTRL_C = _allow_CTRL_C_other
92
92
93
93
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95 # Main InputHookManager class
95 # Main InputHookManager class
96 #-----------------------------------------------------------------------------
96 #-----------------------------------------------------------------------------
97
97
98
98
99 class InputHookManager(object):
99 class InputHookManager(object):
100 """Manage PyOS_InputHook for different GUI toolkits.
100 """Manage PyOS_InputHook for different GUI toolkits.
101
101
102 This class installs various hooks under ``PyOSInputHook`` to handle
102 This class installs various hooks under ``PyOSInputHook`` to handle
103 GUI event loop integration.
103 GUI event loop integration.
104 """
104 """
105
105
106 def __init__(self):
106 def __init__(self):
107 if ctypes is None:
107 if ctypes is None:
108 warn("IPython GUI event loop requires ctypes, %gui will not be available")
108 warn("IPython GUI event loop requires ctypes, %gui will not be available")
109 return
109 return
110 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
110 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
111 self._apps = {}
111 self._apps = {}
112 self._reset()
112 self._reset()
113
113
114 def _reset(self):
114 def _reset(self):
115 self._callback_pyfunctype = None
115 self._callback_pyfunctype = None
116 self._callback = None
116 self._callback = None
117 self._installed = False
117 self._installed = False
118 self._current_gui = None
118 self._current_gui = None
119
119
120 def get_pyos_inputhook(self):
120 def get_pyos_inputhook(self):
121 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
121 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
122 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
122 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
123
123
124 def get_pyos_inputhook_as_func(self):
124 def get_pyos_inputhook_as_func(self):
125 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
125 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
126 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
126 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
127
127
128 def set_inputhook(self, callback):
128 def set_inputhook(self, callback):
129 """Set PyOS_InputHook to callback and return the previous one."""
129 """Set PyOS_InputHook to callback and return the previous one."""
130 # On platforms with 'readline' support, it's all too likely to
130 # On platforms with 'readline' support, it's all too likely to
131 # have a KeyboardInterrupt signal delivered *even before* an
131 # have a KeyboardInterrupt signal delivered *even before* an
132 # initial ``try:`` clause in the callback can be executed, so
132 # initial ``try:`` clause in the callback can be executed, so
133 # we need to disable CTRL+C in this situation.
133 # we need to disable CTRL+C in this situation.
134 ignore_CTRL_C()
134 ignore_CTRL_C()
135 self._callback = callback
135 self._callback = callback
136 self._callback_pyfunctype = self.PYFUNC(callback)
136 self._callback_pyfunctype = self.PYFUNC(callback)
137 pyos_inputhook_ptr = self.get_pyos_inputhook()
137 pyos_inputhook_ptr = self.get_pyos_inputhook()
138 original = self.get_pyos_inputhook_as_func()
138 original = self.get_pyos_inputhook_as_func()
139 pyos_inputhook_ptr.value = \
139 pyos_inputhook_ptr.value = \
140 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
140 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
141 self._installed = True
141 self._installed = True
142 return original
142 return original
143
143
144 def clear_inputhook(self, app=None):
144 def clear_inputhook(self, app=None):
145 """Set PyOS_InputHook to NULL and return the previous one.
145 """Set PyOS_InputHook to NULL and return the previous one.
146
146
147 Parameters
147 Parameters
148 ----------
148 ----------
149 app : optional, ignored
149 app : optional, ignored
150 This parameter is allowed only so that clear_inputhook() can be
150 This parameter is allowed only so that clear_inputhook() can be
151 called with a similar interface as all the ``enable_*`` methods. But
151 called with a similar interface as all the ``enable_*`` methods. But
152 the actual value of the parameter is ignored. This uniform interface
152 the actual value of the parameter is ignored. This uniform interface
153 makes it easier to have user-level entry points in the main IPython
153 makes it easier to have user-level entry points in the main IPython
154 app like :meth:`enable_gui`."""
154 app like :meth:`enable_gui`."""
155 pyos_inputhook_ptr = self.get_pyos_inputhook()
155 pyos_inputhook_ptr = self.get_pyos_inputhook()
156 original = self.get_pyos_inputhook_as_func()
156 original = self.get_pyos_inputhook_as_func()
157 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
157 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
158 allow_CTRL_C()
158 allow_CTRL_C()
159 self._reset()
159 self._reset()
160 return original
160 return original
161
161
162 def clear_app_refs(self, gui=None):
162 def clear_app_refs(self, gui=None):
163 """Clear IPython's internal reference to an application instance.
163 """Clear IPython's internal reference to an application instance.
164
164
165 Whenever we create an app for a user on qt4 or wx, we hold a
165 Whenever we create an app for a user on qt4 or wx, we hold a
166 reference to the app. This is needed because in some cases bad things
166 reference to the app. This is needed because in some cases bad things
167 can happen if a user doesn't hold a reference themselves. This
167 can happen if a user doesn't hold a reference themselves. This
168 method is provided to clear the references we are holding.
168 method is provided to clear the references we are holding.
169
169
170 Parameters
170 Parameters
171 ----------
171 ----------
172 gui : None or str
172 gui : None or str
173 If None, clear all app references. If ('wx', 'qt4') clear
173 If None, clear all app references. If ('wx', 'qt4') clear
174 the app for that toolkit. References are not held for gtk or tk
174 the app for that toolkit. References are not held for gtk or tk
175 as those toolkits don't have the notion of an app.
175 as those toolkits don't have the notion of an app.
176 """
176 """
177 if gui is None:
177 if gui is None:
178 self._apps = {}
178 self._apps = {}
179 elif gui in self._apps:
179 elif gui in self._apps:
180 del self._apps[gui]
180 del self._apps[gui]
181
181
182 def enable_wx(self, app=None):
182 def enable_wx(self, app=None):
183 """Enable event loop integration with wxPython.
183 """Enable event loop integration with wxPython.
184
184
185 Parameters
185 Parameters
186 ----------
186 ----------
187 app : WX Application, optional.
187 app : WX Application, optional.
188 Running application to use. If not given, we probe WX for an
188 Running application to use. If not given, we probe WX for an
189 existing application object, and create a new one if none is found.
189 existing application object, and create a new one if none is found.
190
190
191 Notes
191 Notes
192 -----
192 -----
193 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
193 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
194 the wxPython to integrate with terminal based applications like
194 the wxPython to integrate with terminal based applications like
195 IPython.
195 IPython.
196
196
197 If ``app`` is not given we probe for an existing one, and return it if
197 If ``app`` is not given we probe for an existing one, and return it if
198 found. If no existing app is found, we create an :class:`wx.App` as
198 found. If no existing app is found, we create an :class:`wx.App` as
199 follows::
199 follows::
200
200
201 import wx
201 import wx
202 app = wx.App(redirect=False, clearSigInt=False)
202 app = wx.App(redirect=False, clearSigInt=False)
203 """
203 """
204 import wx
204 import wx
205
205
206 wx_version = V(wx.__version__).version
206 wx_version = V(wx.__version__).version
207
207
208 if wx_version < [2, 8]:
208 if wx_version < [2, 8]:
209 raise ValueError("requires wxPython >= 2.8, but you have %s" % wx.__version__)
209 raise ValueError("requires wxPython >= 2.8, but you have %s" % wx.__version__)
210
210
211 from IPython.lib.inputhookwx import inputhook_wx
211 from IPython.lib.inputhookwx import inputhook_wx
212 self.set_inputhook(inputhook_wx)
212 self.set_inputhook(inputhook_wx)
213 self._current_gui = GUI_WX
213 self._current_gui = GUI_WX
214 import wx
214 import wx
215 if app is None:
215 if app is None:
216 app = wx.GetApp()
216 app = wx.GetApp()
217 if app is None:
217 if app is None:
218 app = wx.App(redirect=False, clearSigInt=False)
218 app = wx.App(redirect=False, clearSigInt=False)
219 app._in_event_loop = True
219 app._in_event_loop = True
220 self._apps[GUI_WX] = app
220 self._apps[GUI_WX] = app
221 return app
221 return app
222
222
223 def disable_wx(self):
223 def disable_wx(self):
224 """Disable event loop integration with wxPython.
224 """Disable event loop integration with wxPython.
225
225
226 This merely sets PyOS_InputHook to NULL.
226 This merely sets PyOS_InputHook to NULL.
227 """
227 """
228 if GUI_WX in self._apps:
228 if GUI_WX in self._apps:
229 self._apps[GUI_WX]._in_event_loop = False
229 self._apps[GUI_WX]._in_event_loop = False
230 self.clear_inputhook()
230 self.clear_inputhook()
231
231
232 def enable_qt4(self, app=None):
232 def enable_qt4(self, app=None):
233 """Enable event loop integration with PyQt4.
233 """Enable event loop integration with PyQt4.
234
234
235 Parameters
235 Parameters
236 ----------
236 ----------
237 app : Qt Application, optional.
237 app : Qt Application, optional.
238 Running application to use. If not given, we probe Qt for an
238 Running application to use. If not given, we probe Qt for an
239 existing application object, and create a new one if none is found.
239 existing application object, and create a new one if none is found.
240
240
241 Notes
241 Notes
242 -----
242 -----
243 This methods sets the PyOS_InputHook for PyQt4, which allows
243 This methods sets the PyOS_InputHook for PyQt4, which allows
244 the PyQt4 to integrate with terminal based applications like
244 the PyQt4 to integrate with terminal based applications like
245 IPython.
245 IPython.
246
246
247 If ``app`` is not given we probe for an existing one, and return it if
247 If ``app`` is not given we probe for an existing one, and return it if
248 found. If no existing app is found, we create an :class:`QApplication`
248 found. If no existing app is found, we create an :class:`QApplication`
249 as follows::
249 as follows::
250
250
251 from PyQt4 import QtCore
251 from PyQt4 import QtCore
252 app = QtGui.QApplication(sys.argv)
252 app = QtGui.QApplication(sys.argv)
253 """
253 """
254 from IPython.lib.inputhookqt4 import create_inputhook_qt4
254 from IPython.lib.inputhookqt4 import create_inputhook_qt4
255 app, inputhook_qt4 = create_inputhook_qt4(self, app)
255 app, inputhook_qt4 = create_inputhook_qt4(self, app)
256 self.set_inputhook(inputhook_qt4)
256 self.set_inputhook(inputhook_qt4)
257
257
258 self._current_gui = GUI_QT4
258 self._current_gui = GUI_QT4
259 app._in_event_loop = True
259 app._in_event_loop = True
260 self._apps[GUI_QT4] = app
260 self._apps[GUI_QT4] = app
261 return app
261 return app
262
262
263 def disable_qt4(self):
263 def disable_qt4(self):
264 """Disable event loop integration with PyQt4.
264 """Disable event loop integration with PyQt4.
265
265
266 This merely sets PyOS_InputHook to NULL.
266 This merely sets PyOS_InputHook to NULL.
267 """
267 """
268 if GUI_QT4 in self._apps:
268 if GUI_QT4 in self._apps:
269 self._apps[GUI_QT4]._in_event_loop = False
269 self._apps[GUI_QT4]._in_event_loop = False
270 self.clear_inputhook()
270 self.clear_inputhook()
271
271
272 def enable_gtk(self, app=None):
272 def enable_gtk(self, app=None):
273 """Enable event loop integration with PyGTK.
273 """Enable event loop integration with PyGTK.
274
274
275 Parameters
275 Parameters
276 ----------
276 ----------
277 app : ignored
277 app : ignored
278 Ignored, it's only a placeholder to keep the call signature of all
278 Ignored, it's only a placeholder to keep the call signature of all
279 gui activation methods consistent, which simplifies the logic of
279 gui activation methods consistent, which simplifies the logic of
280 supporting magics.
280 supporting magics.
281
281
282 Notes
282 Notes
283 -----
283 -----
284 This methods sets the PyOS_InputHook for PyGTK, which allows
284 This methods sets the PyOS_InputHook for PyGTK, which allows
285 the PyGTK to integrate with terminal based applications like
285 the PyGTK to integrate with terminal based applications like
286 IPython.
286 IPython.
287 """
287 """
288 import gtk
288 import gtk
289 try:
289 try:
290 gtk.set_interactive(True)
290 gtk.set_interactive(True)
291 self._current_gui = GUI_GTK
291 self._current_gui = GUI_GTK
292 except AttributeError:
292 except AttributeError:
293 # For older versions of gtk, use our own ctypes version
293 # For older versions of gtk, use our own ctypes version
294 from IPython.lib.inputhookgtk import inputhook_gtk
294 from IPython.lib.inputhookgtk import inputhook_gtk
295 self.set_inputhook(inputhook_gtk)
295 self.set_inputhook(inputhook_gtk)
296 self._current_gui = GUI_GTK
296 self._current_gui = GUI_GTK
297
297
298 def disable_gtk(self):
298 def disable_gtk(self):
299 """Disable event loop integration with PyGTK.
299 """Disable event loop integration with PyGTK.
300
300
301 This merely sets PyOS_InputHook to NULL.
301 This merely sets PyOS_InputHook to NULL.
302 """
302 """
303 self.clear_inputhook()
303 self.clear_inputhook()
304
304
305 def enable_tk(self, app=None):
305 def enable_tk(self, app=None):
306 """Enable event loop integration with Tk.
306 """Enable event loop integration with Tk.
307
307
308 Parameters
308 Parameters
309 ----------
309 ----------
310 app : toplevel :class:`Tkinter.Tk` widget, optional.
310 app : toplevel :class:`Tkinter.Tk` widget, optional.
311 Running toplevel widget to use. If not given, we probe Tk for an
311 Running toplevel widget to use. If not given, we probe Tk for an
312 existing one, and create a new one if none is found.
312 existing one, and create a new one if none is found.
313
313
314 Notes
314 Notes
315 -----
315 -----
316 If you have already created a :class:`Tkinter.Tk` object, the only
316 If you have already created a :class:`Tkinter.Tk` object, the only
317 thing done by this method is to register with the
317 thing done by this method is to register with the
318 :class:`InputHookManager`, since creating that object automatically
318 :class:`InputHookManager`, since creating that object automatically
319 sets ``PyOS_InputHook``.
319 sets ``PyOS_InputHook``.
320 """
320 """
321 self._current_gui = GUI_TK
321 self._current_gui = GUI_TK
322 if app is None:
322 if app is None:
323 import Tkinter
323 import Tkinter
324 app = Tkinter.Tk()
324 app = Tkinter.Tk()
325 app.withdraw()
325 app.withdraw()
326 self._apps[GUI_TK] = app
326 self._apps[GUI_TK] = app
327 return app
327 return app
328
328
329 def disable_tk(self):
329 def disable_tk(self):
330 """Disable event loop integration with Tkinter.
330 """Disable event loop integration with Tkinter.
331
331
332 This merely sets PyOS_InputHook to NULL.
332 This merely sets PyOS_InputHook to NULL.
333 """
333 """
334 self.clear_inputhook()
334 self.clear_inputhook()
335
335
336
336
337 def enable_glut(self, app=None):
337 def enable_glut(self, app=None):
338 """ Enable event loop integration with GLUT.
338 """ Enable event loop integration with GLUT.
339
339
340 Parameters
340 Parameters
341 ----------
341 ----------
342
342
343 app : ignored
343 app : ignored
344 Ignored, it's only a placeholder to keep the call signature of all
344 Ignored, it's only a placeholder to keep the call signature of all
345 gui activation methods consistent, which simplifies the logic of
345 gui activation methods consistent, which simplifies the logic of
346 supporting magics.
346 supporting magics.
347
347
348 Notes
348 Notes
349 -----
349 -----
350
350
351 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
351 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
352 integrate with terminal based applications like IPython. Due to GLUT
352 integrate with terminal based applications like IPython. Due to GLUT
353 limitations, it is currently not possible to start the event loop
353 limitations, it is currently not possible to start the event loop
354 without first creating a window. You should thus not create another
354 without first creating a window. You should thus not create another
355 window but use instead the created one. See 'gui-glut.py' in the
355 window but use instead the created one. See 'gui-glut.py' in the
356 docs/examples/lib directory.
356 docs/examples/lib directory.
357
357
358 The default screen mode is set to:
358 The default screen mode is set to:
359 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
359 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
360 """
360 """
361
361
362 import OpenGL.GLUT as glut
362 import OpenGL.GLUT as glut
363 from IPython.lib.inputhookglut import glut_display_mode, \
363 from IPython.lib.inputhookglut import glut_display_mode, \
364 glut_close, glut_display, \
364 glut_close, glut_display, \
365 glut_idle, inputhook_glut
365 glut_idle, inputhook_glut
366
366
367 if GUI_GLUT not in self._apps:
367 if GUI_GLUT not in self._apps:
368 glut.glutInit( sys.argv )
368 glut.glutInit( sys.argv )
369 glut.glutInitDisplayMode( glut_display_mode )
369 glut.glutInitDisplayMode( glut_display_mode )
370 # This is specific to freeglut
370 # This is specific to freeglut
371 if bool(glut.glutSetOption):
371 if bool(glut.glutSetOption):
372 glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
372 glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
373 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
373 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
374 glut.glutCreateWindow( sys.argv[0] )
374 glut.glutCreateWindow( sys.argv[0] )
375 glut.glutReshapeWindow( 1, 1 )
375 glut.glutReshapeWindow( 1, 1 )
376 glut.glutHideWindow( )
376 glut.glutHideWindow( )
377 glut.glutWMCloseFunc( glut_close )
377 glut.glutWMCloseFunc( glut_close )
378 glut.glutDisplayFunc( glut_display )
378 glut.glutDisplayFunc( glut_display )
379 glut.glutIdleFunc( glut_idle )
379 glut.glutIdleFunc( glut_idle )
380 else:
380 else:
381 glut.glutWMCloseFunc( glut_close )
381 glut.glutWMCloseFunc( glut_close )
382 glut.glutDisplayFunc( glut_display )
382 glut.glutDisplayFunc( glut_display )
383 glut.glutIdleFunc( glut_idle)
383 glut.glutIdleFunc( glut_idle)
384 self.set_inputhook( inputhook_glut )
384 self.set_inputhook( inputhook_glut )
385 self._current_gui = GUI_GLUT
385 self._current_gui = GUI_GLUT
386 self._apps[GUI_GLUT] = True
386 self._apps[GUI_GLUT] = True
387
387
388
388
389 def disable_glut(self):
389 def disable_glut(self):
390 """Disable event loop integration with glut.
390 """Disable event loop integration with glut.
391
391
392 This sets PyOS_InputHook to NULL and set the display function to a
392 This sets PyOS_InputHook to NULL and set the display function to a
393 dummy one and set the timer to a dummy timer that will be triggered
393 dummy one and set the timer to a dummy timer that will be triggered
394 very far in the future.
394 very far in the future.
395 """
395 """
396 import OpenGL.GLUT as glut
396 import OpenGL.GLUT as glut
397 from glut_support import glutMainLoopEvent
397 from glut_support import glutMainLoopEvent
398
398
399 glut.glutHideWindow() # This is an event to be processed below
399 glut.glutHideWindow() # This is an event to be processed below
400 glutMainLoopEvent()
400 glutMainLoopEvent()
401 self.clear_inputhook()
401 self.clear_inputhook()
402
402
403 def enable_pyglet(self, app=None):
403 def enable_pyglet(self, app=None):
404 """Enable event loop integration with pyglet.
404 """Enable event loop integration with pyglet.
405
405
406 Parameters
406 Parameters
407 ----------
407 ----------
408 app : ignored
408 app : ignored
409 Ignored, it's only a placeholder to keep the call signature of all
409 Ignored, it's only a placeholder to keep the call signature of all
410 gui activation methods consistent, which simplifies the logic of
410 gui activation methods consistent, which simplifies the logic of
411 supporting magics.
411 supporting magics.
412
412
413 Notes
413 Notes
414 -----
414 -----
415 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
415 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
416 pyglet to integrate with terminal based applications like
416 pyglet to integrate with terminal based applications like
417 IPython.
417 IPython.
418
418
419 """
419 """
420 import pyglet
421 from IPython.lib.inputhookpyglet import inputhook_pyglet
420 from IPython.lib.inputhookpyglet import inputhook_pyglet
422 self.set_inputhook(inputhook_pyglet)
421 self.set_inputhook(inputhook_pyglet)
423 self._current_gui = GUI_PYGLET
422 self._current_gui = GUI_PYGLET
424 return app
423 return app
425
424
426 def disable_pyglet(self):
425 def disable_pyglet(self):
427 """Disable event loop integration with pyglet.
426 """Disable event loop integration with pyglet.
428
427
429 This merely sets PyOS_InputHook to NULL.
428 This merely sets PyOS_InputHook to NULL.
430 """
429 """
431 self.clear_inputhook()
430 self.clear_inputhook()
432
431
433 def enable_gtk3(self, app=None):
432 def enable_gtk3(self, app=None):
434 """Enable event loop integration with Gtk3 (gir bindings).
433 """Enable event loop integration with Gtk3 (gir bindings).
435
434
436 Parameters
435 Parameters
437 ----------
436 ----------
438 app : ignored
437 app : ignored
439 Ignored, it's only a placeholder to keep the call signature of all
438 Ignored, it's only a placeholder to keep the call signature of all
440 gui activation methods consistent, which simplifies the logic of
439 gui activation methods consistent, which simplifies the logic of
441 supporting magics.
440 supporting magics.
442
441
443 Notes
442 Notes
444 -----
443 -----
445 This methods sets the PyOS_InputHook for Gtk3, which allows
444 This methods sets the PyOS_InputHook for Gtk3, which allows
446 the Gtk3 to integrate with terminal based applications like
445 the Gtk3 to integrate with terminal based applications like
447 IPython.
446 IPython.
448 """
447 """
449 from IPython.lib.inputhookgtk3 import inputhook_gtk3
448 from IPython.lib.inputhookgtk3 import inputhook_gtk3
450 self.set_inputhook(inputhook_gtk3)
449 self.set_inputhook(inputhook_gtk3)
451 self._current_gui = GUI_GTK
450 self._current_gui = GUI_GTK
452
451
453 def disable_gtk3(self):
452 def disable_gtk3(self):
454 """Disable event loop integration with PyGTK.
453 """Disable event loop integration with PyGTK.
455
454
456 This merely sets PyOS_InputHook to NULL.
455 This merely sets PyOS_InputHook to NULL.
457 """
456 """
458 self.clear_inputhook()
457 self.clear_inputhook()
459
458
460 def current_gui(self):
459 def current_gui(self):
461 """Return a string indicating the currently active GUI or None."""
460 """Return a string indicating the currently active GUI or None."""
462 return self._current_gui
461 return self._current_gui
463
462
464 inputhook_manager = InputHookManager()
463 inputhook_manager = InputHookManager()
465
464
466 enable_wx = inputhook_manager.enable_wx
465 enable_wx = inputhook_manager.enable_wx
467 disable_wx = inputhook_manager.disable_wx
466 disable_wx = inputhook_manager.disable_wx
468 enable_qt4 = inputhook_manager.enable_qt4
467 enable_qt4 = inputhook_manager.enable_qt4
469 disable_qt4 = inputhook_manager.disable_qt4
468 disable_qt4 = inputhook_manager.disable_qt4
470 enable_gtk = inputhook_manager.enable_gtk
469 enable_gtk = inputhook_manager.enable_gtk
471 disable_gtk = inputhook_manager.disable_gtk
470 disable_gtk = inputhook_manager.disable_gtk
472 enable_tk = inputhook_manager.enable_tk
471 enable_tk = inputhook_manager.enable_tk
473 disable_tk = inputhook_manager.disable_tk
472 disable_tk = inputhook_manager.disable_tk
474 enable_glut = inputhook_manager.enable_glut
473 enable_glut = inputhook_manager.enable_glut
475 disable_glut = inputhook_manager.disable_glut
474 disable_glut = inputhook_manager.disable_glut
476 enable_pyglet = inputhook_manager.enable_pyglet
475 enable_pyglet = inputhook_manager.enable_pyglet
477 disable_pyglet = inputhook_manager.disable_pyglet
476 disable_pyglet = inputhook_manager.disable_pyglet
478 enable_gtk3 = inputhook_manager.enable_gtk3
477 enable_gtk3 = inputhook_manager.enable_gtk3
479 disable_gtk3 = inputhook_manager.disable_gtk3
478 disable_gtk3 = inputhook_manager.disable_gtk3
480 clear_inputhook = inputhook_manager.clear_inputhook
479 clear_inputhook = inputhook_manager.clear_inputhook
481 set_inputhook = inputhook_manager.set_inputhook
480 set_inputhook = inputhook_manager.set_inputhook
482 current_gui = inputhook_manager.current_gui
481 current_gui = inputhook_manager.current_gui
483 clear_app_refs = inputhook_manager.clear_app_refs
482 clear_app_refs = inputhook_manager.clear_app_refs
484
483
485
484
486 # Convenience function to switch amongst them
485 # Convenience function to switch amongst them
487 def enable_gui(gui=None, app=None):
486 def enable_gui(gui=None, app=None):
488 """Switch amongst GUI input hooks by name.
487 """Switch amongst GUI input hooks by name.
489
488
490 This is just a utility wrapper around the methods of the InputHookManager
489 This is just a utility wrapper around the methods of the InputHookManager
491 object.
490 object.
492
491
493 Parameters
492 Parameters
494 ----------
493 ----------
495 gui : optional, string or None
494 gui : optional, string or None
496 If None (or 'none'), clears input hook, otherwise it must be one
495 If None (or 'none'), clears input hook, otherwise it must be one
497 of the recognized GUI names (see ``GUI_*`` constants in module).
496 of the recognized GUI names (see ``GUI_*`` constants in module).
498
497
499 app : optional, existing application object.
498 app : optional, existing application object.
500 For toolkits that have the concept of a global app, you can supply an
499 For toolkits that have the concept of a global app, you can supply an
501 existing one. If not given, the toolkit will be probed for one, and if
500 existing one. If not given, the toolkit will be probed for one, and if
502 none is found, a new one will be created. Note that GTK does not have
501 none is found, a new one will be created. Note that GTK does not have
503 this concept, and passing an app if ``gui=="GTK"`` will raise an error.
502 this concept, and passing an app if ``gui=="GTK"`` will raise an error.
504
503
505 Returns
504 Returns
506 -------
505 -------
507 The output of the underlying gui switch routine, typically the actual
506 The output of the underlying gui switch routine, typically the actual
508 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
507 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
509 one.
508 one.
510 """
509 """
511 guis = {None: clear_inputhook,
510 guis = {None: clear_inputhook,
512 GUI_NONE: clear_inputhook,
511 GUI_NONE: clear_inputhook,
513 GUI_OSX: lambda app=False: None,
512 GUI_OSX: lambda app=False: None,
514 GUI_TK: enable_tk,
513 GUI_TK: enable_tk,
515 GUI_GTK: enable_gtk,
514 GUI_GTK: enable_gtk,
516 GUI_WX: enable_wx,
515 GUI_WX: enable_wx,
517 GUI_QT: enable_qt4, # qt3 not supported
516 GUI_QT: enable_qt4, # qt3 not supported
518 GUI_QT4: enable_qt4,
517 GUI_QT4: enable_qt4,
519 GUI_GLUT: enable_glut,
518 GUI_GLUT: enable_glut,
520 GUI_PYGLET: enable_pyglet,
519 GUI_PYGLET: enable_pyglet,
521 GUI_GTK3: enable_gtk3,
520 GUI_GTK3: enable_gtk3,
522 }
521 }
523 try:
522 try:
524 gui_hook = guis[gui]
523 gui_hook = guis[gui]
525 except KeyError:
524 except KeyError:
526 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
525 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
527 raise ValueError(e)
526 raise ValueError(e)
528 return gui_hook(app)
527 return gui_hook(app)
529
528
@@ -1,176 +1,175 b''
1 # coding: utf-8
1 # coding: utf-8
2 """
2 """
3 GLUT Inputhook support functions
3 GLUT Inputhook support functions
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 # GLUT is quite an old library and it is difficult to ensure proper
13 # GLUT is quite an old library and it is difficult to ensure proper
14 # integration within IPython since original GLUT does not allow to handle
14 # integration within IPython since original GLUT does not allow to handle
15 # events one by one. Instead, it requires for the mainloop to be entered
15 # events one by one. Instead, it requires for the mainloop to be entered
16 # and never returned (there is not even a function to exit he
16 # and never returned (there is not even a function to exit he
17 # mainloop). Fortunately, there are alternatives such as freeglut
17 # mainloop). Fortunately, there are alternatives such as freeglut
18 # (available for linux and windows) and the OSX implementation gives
18 # (available for linux and windows) and the OSX implementation gives
19 # access to a glutCheckLoop() function that blocks itself until a new
19 # access to a glutCheckLoop() function that blocks itself until a new
20 # event is received. This means we have to setup the idle callback to
20 # event is received. This means we have to setup the idle callback to
21 # ensure we got at least one event that will unblock the function.
21 # ensure we got at least one event that will unblock the function.
22 #
22 #
23 # Furthermore, it is not possible to install these handlers without a window
23 # Furthermore, it is not possible to install these handlers without a window
24 # being first created. We choose to make this window invisible. This means that
24 # being first created. We choose to make this window invisible. This means that
25 # display mode options are set at this level and user won't be able to change
25 # display mode options are set at this level and user won't be able to change
26 # them later without modifying the code. This should probably be made available
26 # them later without modifying the code. This should probably be made available
27 # via IPython options system.
27 # via IPython options system.
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Imports
30 # Imports
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 import os
32 import os
33 import sys
33 import sys
34 import time
34 import time
35 import signal
35 import signal
36 import OpenGL
37 import OpenGL.GLUT as glut
36 import OpenGL.GLUT as glut
38 import OpenGL.platform as platform
37 import OpenGL.platform as platform
39 from timeit import default_timer as clock
38 from timeit import default_timer as clock
40
39
41 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
42 # Constants
41 # Constants
43 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
44
43
45 # Frame per second : 60
44 # Frame per second : 60
46 # Should probably be an IPython option
45 # Should probably be an IPython option
47 glut_fps = 60
46 glut_fps = 60
48
47
49
48
50 # Display mode : double buffeed + rgba + depth
49 # Display mode : double buffeed + rgba + depth
51 # Should probably be an IPython option
50 # Should probably be an IPython option
52 glut_display_mode = (glut.GLUT_DOUBLE |
51 glut_display_mode = (glut.GLUT_DOUBLE |
53 glut.GLUT_RGBA |
52 glut.GLUT_RGBA |
54 glut.GLUT_DEPTH)
53 glut.GLUT_DEPTH)
55
54
56 glutMainLoopEvent = None
55 glutMainLoopEvent = None
57 if sys.platform == 'darwin':
56 if sys.platform == 'darwin':
58 try:
57 try:
59 glutCheckLoop = platform.createBaseFunction(
58 glutCheckLoop = platform.createBaseFunction(
60 'glutCheckLoop', dll=platform.GLUT, resultType=None,
59 'glutCheckLoop', dll=platform.GLUT, resultType=None,
61 argTypes=[],
60 argTypes=[],
62 doc='glutCheckLoop( ) -> None',
61 doc='glutCheckLoop( ) -> None',
63 argNames=(),
62 argNames=(),
64 )
63 )
65 except AttributeError:
64 except AttributeError:
66 raise RuntimeError(
65 raise RuntimeError(
67 '''Your glut implementation does not allow interactive sessions'''
66 '''Your glut implementation does not allow interactive sessions'''
68 '''Consider installing freeglut.''')
67 '''Consider installing freeglut.''')
69 glutMainLoopEvent = glutCheckLoop
68 glutMainLoopEvent = glutCheckLoop
70 elif glut.HAVE_FREEGLUT:
69 elif glut.HAVE_FREEGLUT:
71 glutMainLoopEvent = glut.glutMainLoopEvent
70 glutMainLoopEvent = glut.glutMainLoopEvent
72 else:
71 else:
73 raise RuntimeError(
72 raise RuntimeError(
74 '''Your glut implementation does not allow interactive sessions. '''
73 '''Your glut implementation does not allow interactive sessions. '''
75 '''Consider installing freeglut.''')
74 '''Consider installing freeglut.''')
76
75
77
76
78 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
79 # Platform-dependent imports and functions
78 # Platform-dependent imports and functions
80 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
81
80
82 if os.name == 'posix':
81 if os.name == 'posix':
83 import select
82 import select
84
83
85 def stdin_ready():
84 def stdin_ready():
86 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
85 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
87 if infds:
86 if infds:
88 return True
87 return True
89 else:
88 else:
90 return False
89 return False
91
90
92 elif sys.platform == 'win32':
91 elif sys.platform == 'win32':
93 import msvcrt
92 import msvcrt
94
93
95 def stdin_ready():
94 def stdin_ready():
96 return msvcrt.kbhit()
95 return msvcrt.kbhit()
97
96
98 #-----------------------------------------------------------------------------
97 #-----------------------------------------------------------------------------
99 # Callback functions
98 # Callback functions
100 #-----------------------------------------------------------------------------
99 #-----------------------------------------------------------------------------
101
100
102 def glut_display():
101 def glut_display():
103 # Dummy display function
102 # Dummy display function
104 pass
103 pass
105
104
106 def glut_idle():
105 def glut_idle():
107 # Dummy idle function
106 # Dummy idle function
108 pass
107 pass
109
108
110 def glut_close():
109 def glut_close():
111 # Close function only hides the current window
110 # Close function only hides the current window
112 glut.glutHideWindow()
111 glut.glutHideWindow()
113 glutMainLoopEvent()
112 glutMainLoopEvent()
114
113
115 def glut_int_handler(signum, frame):
114 def glut_int_handler(signum, frame):
116 # Catch sigint and print the defautl message
115 # Catch sigint and print the defautl message
117 signal.signal(signal.SIGINT, signal.default_int_handler)
116 signal.signal(signal.SIGINT, signal.default_int_handler)
118 print '\nKeyboardInterrupt'
117 print '\nKeyboardInterrupt'
119 # Need to reprint the prompt at this stage
118 # Need to reprint the prompt at this stage
120
119
121
120
122
121
123 #-----------------------------------------------------------------------------
122 #-----------------------------------------------------------------------------
124 # Code
123 # Code
125 #-----------------------------------------------------------------------------
124 #-----------------------------------------------------------------------------
126 def inputhook_glut():
125 def inputhook_glut():
127 """Run the pyglet event loop by processing pending events only.
126 """Run the pyglet event loop by processing pending events only.
128
127
129 This keeps processing pending events until stdin is ready. After
128 This keeps processing pending events until stdin is ready. After
130 processing all pending events, a call to time.sleep is inserted. This is
129 processing all pending events, a call to time.sleep is inserted. This is
131 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
130 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
132 though for best performance.
131 though for best performance.
133 """
132 """
134 # We need to protect against a user pressing Control-C when IPython is
133 # We need to protect against a user pressing Control-C when IPython is
135 # idle and this is running. We trap KeyboardInterrupt and pass.
134 # idle and this is running. We trap KeyboardInterrupt and pass.
136
135
137 signal.signal(signal.SIGINT, glut_int_handler)
136 signal.signal(signal.SIGINT, glut_int_handler)
138
137
139 try:
138 try:
140 t = clock()
139 t = clock()
141
140
142 # Make sure the default window is set after a window has been closed
141 # Make sure the default window is set after a window has been closed
143 if glut.glutGetWindow() == 0:
142 if glut.glutGetWindow() == 0:
144 glut.glutSetWindow( 1 )
143 glut.glutSetWindow( 1 )
145 glutMainLoopEvent()
144 glutMainLoopEvent()
146 return 0
145 return 0
147
146
148 while not stdin_ready():
147 while not stdin_ready():
149 glutMainLoopEvent()
148 glutMainLoopEvent()
150 # We need to sleep at this point to keep the idle CPU load
149 # We need to sleep at this point to keep the idle CPU load
151 # low. However, if sleep to long, GUI response is poor. As
150 # low. However, if sleep to long, GUI response is poor. As
152 # a compromise, we watch how often GUI events are being processed
151 # a compromise, we watch how often GUI events are being processed
153 # and switch between a short and long sleep time. Here are some
152 # and switch between a short and long sleep time. Here are some
154 # stats useful in helping to tune this.
153 # stats useful in helping to tune this.
155 # time CPU load
154 # time CPU load
156 # 0.001 13%
155 # 0.001 13%
157 # 0.005 3%
156 # 0.005 3%
158 # 0.01 1.5%
157 # 0.01 1.5%
159 # 0.05 0.5%
158 # 0.05 0.5%
160 used_time = clock() - t
159 used_time = clock() - t
161 if used_time > 5*60.0:
160 if used_time > 5*60.0:
162 # print 'Sleep for 5 s' # dbg
161 # print 'Sleep for 5 s' # dbg
163 time.sleep(5.0)
162 time.sleep(5.0)
164 elif used_time > 10.0:
163 elif used_time > 10.0:
165 # print 'Sleep for 1 s' # dbg
164 # print 'Sleep for 1 s' # dbg
166 time.sleep(1.0)
165 time.sleep(1.0)
167 elif used_time > 0.1:
166 elif used_time > 0.1:
168 # Few GUI events coming in, so we can sleep longer
167 # Few GUI events coming in, so we can sleep longer
169 # print 'Sleep for 0.05 s' # dbg
168 # print 'Sleep for 0.05 s' # dbg
170 time.sleep(0.05)
169 time.sleep(0.05)
171 else:
170 else:
172 # Many GUI events coming in, so sleep only very little
171 # Many GUI events coming in, so sleep only very little
173 time.sleep(0.001)
172 time.sleep(0.001)
174 except KeyboardInterrupt:
173 except KeyboardInterrupt:
175 pass
174 pass
176 return 0
175 return 0
@@ -1,115 +1,114 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Enable pyglet to be used interacive by setting PyOS_InputHook.
3 Enable pyglet to be used interacive by setting PyOS_InputHook.
4
4
5 Authors
5 Authors
6 -------
6 -------
7
7
8 * Nicolas P. Rougier
8 * Nicolas P. Rougier
9 * Fernando Perez
9 * Fernando Perez
10 """
10 """
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2011 The IPython Development Team
13 # Copyright (C) 2008-2011 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 import os
23 import os
24 import signal
25 import sys
24 import sys
26 import time
25 import time
27 from timeit import default_timer as clock
26 from timeit import default_timer as clock
28 import pyglet
27 import pyglet
29
28
30 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
31 # Platform-dependent imports and functions
30 # Platform-dependent imports and functions
32 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
33
32
34 if os.name == 'posix':
33 if os.name == 'posix':
35 import select
34 import select
36
35
37 def stdin_ready():
36 def stdin_ready():
38 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
37 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
39 if infds:
38 if infds:
40 return True
39 return True
41 else:
40 else:
42 return False
41 return False
43
42
44 elif sys.platform == 'win32':
43 elif sys.platform == 'win32':
45 import msvcrt
44 import msvcrt
46
45
47 def stdin_ready():
46 def stdin_ready():
48 return msvcrt.kbhit()
47 return msvcrt.kbhit()
49
48
50
49
51 # On linux only, window.flip() has a bug that causes an AttributeError on
50 # On linux only, window.flip() has a bug that causes an AttributeError on
52 # window close. For details, see:
51 # window close. For details, see:
53 # http://groups.google.com/group/pyglet-users/browse_thread/thread/47c1aab9aa4a3d23/c22f9e819826799e?#c22f9e819826799e
52 # http://groups.google.com/group/pyglet-users/browse_thread/thread/47c1aab9aa4a3d23/c22f9e819826799e?#c22f9e819826799e
54
53
55 if sys.platform.startswith('linux'):
54 if sys.platform.startswith('linux'):
56 def flip(window):
55 def flip(window):
57 try:
56 try:
58 window.flip()
57 window.flip()
59 except AttributeError:
58 except AttributeError:
60 pass
59 pass
61 else:
60 else:
62 def flip(window):
61 def flip(window):
63 window.flip()
62 window.flip()
64
63
65 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
66 # Code
65 # Code
67 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
68
67
69 def inputhook_pyglet():
68 def inputhook_pyglet():
70 """Run the pyglet event loop by processing pending events only.
69 """Run the pyglet event loop by processing pending events only.
71
70
72 This keeps processing pending events until stdin is ready. After
71 This keeps processing pending events until stdin is ready. After
73 processing all pending events, a call to time.sleep is inserted. This is
72 processing all pending events, a call to time.sleep is inserted. This is
74 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
73 needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
75 though for best performance.
74 though for best performance.
76 """
75 """
77 # We need to protect against a user pressing Control-C when IPython is
76 # We need to protect against a user pressing Control-C when IPython is
78 # idle and this is running. We trap KeyboardInterrupt and pass.
77 # idle and this is running. We trap KeyboardInterrupt and pass.
79 try:
78 try:
80 t = clock()
79 t = clock()
81 while not stdin_ready():
80 while not stdin_ready():
82 pyglet.clock.tick()
81 pyglet.clock.tick()
83 for window in pyglet.app.windows:
82 for window in pyglet.app.windows:
84 window.switch_to()
83 window.switch_to()
85 window.dispatch_events()
84 window.dispatch_events()
86 window.dispatch_event('on_draw')
85 window.dispatch_event('on_draw')
87 flip(window)
86 flip(window)
88
87
89 # We need to sleep at this point to keep the idle CPU load
88 # We need to sleep at this point to keep the idle CPU load
90 # low. However, if sleep to long, GUI response is poor. As
89 # low. However, if sleep to long, GUI response is poor. As
91 # a compromise, we watch how often GUI events are being processed
90 # a compromise, we watch how often GUI events are being processed
92 # and switch between a short and long sleep time. Here are some
91 # and switch between a short and long sleep time. Here are some
93 # stats useful in helping to tune this.
92 # stats useful in helping to tune this.
94 # time CPU load
93 # time CPU load
95 # 0.001 13%
94 # 0.001 13%
96 # 0.005 3%
95 # 0.005 3%
97 # 0.01 1.5%
96 # 0.01 1.5%
98 # 0.05 0.5%
97 # 0.05 0.5%
99 used_time = clock() - t
98 used_time = clock() - t
100 if used_time > 5*60.0:
99 if used_time > 5*60.0:
101 # print 'Sleep for 5 s' # dbg
100 # print 'Sleep for 5 s' # dbg
102 time.sleep(5.0)
101 time.sleep(5.0)
103 elif used_time > 10.0:
102 elif used_time > 10.0:
104 # print 'Sleep for 1 s' # dbg
103 # print 'Sleep for 1 s' # dbg
105 time.sleep(1.0)
104 time.sleep(1.0)
106 elif used_time > 0.1:
105 elif used_time > 0.1:
107 # Few GUI events coming in, so we can sleep longer
106 # Few GUI events coming in, so we can sleep longer
108 # print 'Sleep for 0.05 s' # dbg
107 # print 'Sleep for 0.05 s' # dbg
109 time.sleep(0.05)
108 time.sleep(0.05)
110 else:
109 else:
111 # Many GUI events coming in, so sleep only very little
110 # Many GUI events coming in, so sleep only very little
112 time.sleep(0.001)
111 time.sleep(0.001)
113 except KeyboardInterrupt:
112 except KeyboardInterrupt:
114 pass
113 pass
115 return 0
114 return 0
@@ -1,181 +1,180 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Qt4's inputhook support function
3 Qt4's inputhook support function
4
4
5 Author: Christian Boos
5 Author: Christian Boos
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
9 # Copyright (C) 2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import os
19 import os
20 import signal
20 import signal
21 import time
22 import threading
21 import threading
23
22
24 from IPython.core.interactiveshell import InteractiveShell
23 from IPython.core.interactiveshell import InteractiveShell
25 from IPython.external.qt_for_kernel import QtCore, QtGui
24 from IPython.external.qt_for_kernel import QtCore, QtGui
26 from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
25 from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
27
26
28 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
29 # Module Globals
28 # Module Globals
30 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
31
30
32 got_kbdint = False
31 got_kbdint = False
33 sigint_timer = None
32 sigint_timer = None
34
33
35 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
36 # Code
35 # Code
37 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
38
37
39 def create_inputhook_qt4(mgr, app=None):
38 def create_inputhook_qt4(mgr, app=None):
40 """Create an input hook for running the Qt4 application event loop.
39 """Create an input hook for running the Qt4 application event loop.
41
40
42 Parameters
41 Parameters
43 ----------
42 ----------
44 mgr : an InputHookManager
43 mgr : an InputHookManager
45
44
46 app : Qt Application, optional.
45 app : Qt Application, optional.
47 Running application to use. If not given, we probe Qt for an
46 Running application to use. If not given, we probe Qt for an
48 existing application object, and create a new one if none is found.
47 existing application object, and create a new one if none is found.
49
48
50 Returns
49 Returns
51 -------
50 -------
52 A pair consisting of a Qt Application (either the one given or the
51 A pair consisting of a Qt Application (either the one given or the
53 one found or created) and a inputhook.
52 one found or created) and a inputhook.
54
53
55 Notes
54 Notes
56 -----
55 -----
57 We use a custom input hook instead of PyQt4's default one, as it
56 We use a custom input hook instead of PyQt4's default one, as it
58 interacts better with the readline packages (issue #481).
57 interacts better with the readline packages (issue #481).
59
58
60 The inputhook function works in tandem with a 'pre_prompt_hook'
59 The inputhook function works in tandem with a 'pre_prompt_hook'
61 which automatically restores the hook as an inputhook in case the
60 which automatically restores the hook as an inputhook in case the
62 latter has been temporarily disabled after having intercepted a
61 latter has been temporarily disabled after having intercepted a
63 KeyboardInterrupt.
62 KeyboardInterrupt.
64 """
63 """
65
64
66 if app is None:
65 if app is None:
67 app = QtCore.QCoreApplication.instance()
66 app = QtCore.QCoreApplication.instance()
68 if app is None:
67 if app is None:
69 app = QtGui.QApplication([" "])
68 app = QtGui.QApplication([" "])
70
69
71 # Re-use previously created inputhook if any
70 # Re-use previously created inputhook if any
72 ip = InteractiveShell.instance()
71 ip = InteractiveShell.instance()
73 if hasattr(ip, '_inputhook_qt4'):
72 if hasattr(ip, '_inputhook_qt4'):
74 return app, ip._inputhook_qt4
73 return app, ip._inputhook_qt4
75
74
76 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of
75 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of
77 # hooks (they both share the got_kbdint flag)
76 # hooks (they both share the got_kbdint flag)
78
77
79 def inputhook_qt4():
78 def inputhook_qt4():
80 """PyOS_InputHook python hook for Qt4.
79 """PyOS_InputHook python hook for Qt4.
81
80
82 Process pending Qt events and if there's no pending keyboard
81 Process pending Qt events and if there's no pending keyboard
83 input, spend a short slice of time (50ms) running the Qt event
82 input, spend a short slice of time (50ms) running the Qt event
84 loop.
83 loop.
85
84
86 As a Python ctypes callback can't raise an exception, we catch
85 As a Python ctypes callback can't raise an exception, we catch
87 the KeyboardInterrupt and temporarily deactivate the hook,
86 the KeyboardInterrupt and temporarily deactivate the hook,
88 which will let a *second* CTRL+C be processed normally and go
87 which will let a *second* CTRL+C be processed normally and go
89 back to a clean prompt line.
88 back to a clean prompt line.
90 """
89 """
91 try:
90 try:
92 allow_CTRL_C()
91 allow_CTRL_C()
93 app = QtCore.QCoreApplication.instance()
92 app = QtCore.QCoreApplication.instance()
94 if not app: # shouldn't happen, but safer if it happens anyway...
93 if not app: # shouldn't happen, but safer if it happens anyway...
95 return 0
94 return 0
96 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
95 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
97 if not stdin_ready():
96 if not stdin_ready():
98 # Generally a program would run QCoreApplication::exec()
97 # Generally a program would run QCoreApplication::exec()
99 # from main() to enter and process the Qt event loop until
98 # from main() to enter and process the Qt event loop until
100 # quit() or exit() is called and the program terminates.
99 # quit() or exit() is called and the program terminates.
101 #
100 #
102 # For our input hook integration, we need to repeatedly
101 # For our input hook integration, we need to repeatedly
103 # enter and process the Qt event loop for only a short
102 # enter and process the Qt event loop for only a short
104 # amount of time (say 50ms) to ensure that Python stays
103 # amount of time (say 50ms) to ensure that Python stays
105 # responsive to other user inputs.
104 # responsive to other user inputs.
106 #
105 #
107 # A naive approach would be to repeatedly call
106 # A naive approach would be to repeatedly call
108 # QCoreApplication::exec(), using a timer to quit after a
107 # QCoreApplication::exec(), using a timer to quit after a
109 # short amount of time. Unfortunately, QCoreApplication
108 # short amount of time. Unfortunately, QCoreApplication
110 # emits an aboutToQuit signal before stopping, which has
109 # emits an aboutToQuit signal before stopping, which has
111 # the undesirable effect of closing all modal windows.
110 # the undesirable effect of closing all modal windows.
112 #
111 #
113 # To work around this problem, we instead create a
112 # To work around this problem, we instead create a
114 # QEventLoop and call QEventLoop::exec(). Other than
113 # QEventLoop and call QEventLoop::exec(). Other than
115 # setting some state variables which do not seem to be
114 # setting some state variables which do not seem to be
116 # used anywhere, the only thing QCoreApplication adds is
115 # used anywhere, the only thing QCoreApplication adds is
117 # the aboutToQuit signal which is precisely what we are
116 # the aboutToQuit signal which is precisely what we are
118 # trying to avoid.
117 # trying to avoid.
119 timer = QtCore.QTimer()
118 timer = QtCore.QTimer()
120 event_loop = QtCore.QEventLoop()
119 event_loop = QtCore.QEventLoop()
121 timer.timeout.connect(event_loop.quit)
120 timer.timeout.connect(event_loop.quit)
122 while not stdin_ready():
121 while not stdin_ready():
123 timer.start(50)
122 timer.start(50)
124 event_loop.exec_()
123 event_loop.exec_()
125 timer.stop()
124 timer.stop()
126 except KeyboardInterrupt:
125 except KeyboardInterrupt:
127 global got_kbdint, sigint_timer
126 global got_kbdint, sigint_timer
128
127
129 ignore_CTRL_C()
128 ignore_CTRL_C()
130 got_kbdint = True
129 got_kbdint = True
131 mgr.clear_inputhook()
130 mgr.clear_inputhook()
132
131
133 # This generates a second SIGINT so the user doesn't have to
132 # This generates a second SIGINT so the user doesn't have to
134 # press CTRL+C twice to get a clean prompt.
133 # press CTRL+C twice to get a clean prompt.
135 #
134 #
136 # Since we can't catch the resulting KeyboardInterrupt here
135 # Since we can't catch the resulting KeyboardInterrupt here
137 # (because this is a ctypes callback), we use a timer to
136 # (because this is a ctypes callback), we use a timer to
138 # generate the SIGINT after we leave this callback.
137 # generate the SIGINT after we leave this callback.
139 #
138 #
140 # Unfortunately this doesn't work on Windows (SIGINT kills
139 # Unfortunately this doesn't work on Windows (SIGINT kills
141 # Python and CTRL_C_EVENT doesn't work).
140 # Python and CTRL_C_EVENT doesn't work).
142 if(os.name == 'posix'):
141 if(os.name == 'posix'):
143 pid = os.getpid()
142 pid = os.getpid()
144 if(not sigint_timer):
143 if(not sigint_timer):
145 sigint_timer = threading.Timer(.01, os.kill,
144 sigint_timer = threading.Timer(.01, os.kill,
146 args=[pid, signal.SIGINT] )
145 args=[pid, signal.SIGINT] )
147 sigint_timer.start()
146 sigint_timer.start()
148 else:
147 else:
149 print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
148 print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
150
149
151
150
152 except: # NO exceptions are allowed to escape from a ctypes callback
151 except: # NO exceptions are allowed to escape from a ctypes callback
153 ignore_CTRL_C()
152 ignore_CTRL_C()
154 from traceback import print_exc
153 from traceback import print_exc
155 print_exc()
154 print_exc()
156 print("Got exception from inputhook_qt4, unregistering.")
155 print("Got exception from inputhook_qt4, unregistering.")
157 mgr.clear_inputhook()
156 mgr.clear_inputhook()
158 finally:
157 finally:
159 allow_CTRL_C()
158 allow_CTRL_C()
160 return 0
159 return 0
161
160
162 def preprompthook_qt4(ishell):
161 def preprompthook_qt4(ishell):
163 """'pre_prompt_hook' used to restore the Qt4 input hook
162 """'pre_prompt_hook' used to restore the Qt4 input hook
164
163
165 (in case the latter was temporarily deactivated after a
164 (in case the latter was temporarily deactivated after a
166 CTRL+C)
165 CTRL+C)
167 """
166 """
168 global got_kbdint, sigint_timer
167 global got_kbdint, sigint_timer
169
168
170 if(sigint_timer):
169 if(sigint_timer):
171 sigint_timer.cancel()
170 sigint_timer.cancel()
172 sigint_timer = None
171 sigint_timer = None
173
172
174 if got_kbdint:
173 if got_kbdint:
175 mgr.set_inputhook(inputhook_qt4)
174 mgr.set_inputhook(inputhook_qt4)
176 got_kbdint = False
175 got_kbdint = False
177
176
178 ip._inputhook_qt4 = inputhook_qt4
177 ip._inputhook_qt4 = inputhook_qt4
179 ip.set_hook('pre_prompt_hook', preprompthook_qt4)
178 ip.set_hook('pre_prompt_hook', preprompthook_qt4)
180
179
181 return app, inputhook_qt4
180 return app, inputhook_qt4
@@ -1,91 +1,89 b''
1 """Tests for pylab tools module.
1 """Tests for pylab tools module.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2011, the IPython Development Team.
4 # Copyright (c) 2011, the IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 from __future__ import print_function
14 from __future__ import print_function
15
15
16 # Stdlib imports
16 # Stdlib imports
17 import sys
18 import time
17 import time
19
18
20 # Third-party imports
19 # Third-party imports
21 import nose.tools as nt
20 import nose.tools as nt
22
21
23 # Our own imports
22 # Our own imports
24 from IPython.lib import backgroundjobs as bg
23 from IPython.lib import backgroundjobs as bg
25 from IPython.testing import decorators as dec
26
24
27 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
28 # Globals and constants
26 # Globals and constants
29 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
30 t_short = 0.0001 # very short interval to wait on jobs
28 t_short = 0.0001 # very short interval to wait on jobs
31
29
32 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
33 # Local utilities
31 # Local utilities
34 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
35 def sleeper(interval=t_short, *a, **kw):
33 def sleeper(interval=t_short, *a, **kw):
36 args = dict(interval=interval,
34 args = dict(interval=interval,
37 other_args=a,
35 other_args=a,
38 kw_args=kw)
36 kw_args=kw)
39 time.sleep(interval)
37 time.sleep(interval)
40 return args
38 return args
41
39
42 def crasher(interval=t_short, *a, **kw):
40 def crasher(interval=t_short, *a, **kw):
43 time.sleep(interval)
41 time.sleep(interval)
44 raise Exception("Dead job with interval %s" % interval)
42 raise Exception("Dead job with interval %s" % interval)
45
43
46 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
47 # Classes and functions
45 # Classes and functions
48 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
49
47
50 def test_result():
48 def test_result():
51 """Test job submission and result retrieval"""
49 """Test job submission and result retrieval"""
52 jobs = bg.BackgroundJobManager()
50 jobs = bg.BackgroundJobManager()
53 j = jobs.new(sleeper)
51 j = jobs.new(sleeper)
54 j.join()
52 j.join()
55 nt.assert_equal(j.result['interval'], t_short)
53 nt.assert_equal(j.result['interval'], t_short)
56
54
57
55
58 def test_flush():
56 def test_flush():
59 """Test job control"""
57 """Test job control"""
60 jobs = bg.BackgroundJobManager()
58 jobs = bg.BackgroundJobManager()
61 j = jobs.new(sleeper)
59 j = jobs.new(sleeper)
62 j.join()
60 j.join()
63 nt.assert_equal(len(jobs.completed), 1)
61 nt.assert_equal(len(jobs.completed), 1)
64 nt.assert_equal(len(jobs.dead), 0)
62 nt.assert_equal(len(jobs.dead), 0)
65 jobs.flush()
63 jobs.flush()
66 nt.assert_equal(len(jobs.completed), 0)
64 nt.assert_equal(len(jobs.completed), 0)
67
65
68
66
69 def test_dead():
67 def test_dead():
70 """Test control of dead jobs"""
68 """Test control of dead jobs"""
71 jobs = bg.BackgroundJobManager()
69 jobs = bg.BackgroundJobManager()
72 j = jobs.new(crasher)
70 j = jobs.new(crasher)
73 j.join()
71 j.join()
74 nt.assert_equal(len(jobs.completed), 0)
72 nt.assert_equal(len(jobs.completed), 0)
75 nt.assert_equal(len(jobs.dead), 1)
73 nt.assert_equal(len(jobs.dead), 1)
76 jobs.flush()
74 jobs.flush()
77 nt.assert_equal(len(jobs.dead), 0)
75 nt.assert_equal(len(jobs.dead), 0)
78
76
79
77
80 def test_longer():
78 def test_longer():
81 """Test control of longer-running jobs"""
79 """Test control of longer-running jobs"""
82 jobs = bg.BackgroundJobManager()
80 jobs = bg.BackgroundJobManager()
83 # Sleep for long enough for the following two checks to still report the
81 # Sleep for long enough for the following two checks to still report the
84 # job as running, but not so long that it makes the test suite noticeably
82 # job as running, but not so long that it makes the test suite noticeably
85 # slower.
83 # slower.
86 j = jobs.new(sleeper, 0.1)
84 j = jobs.new(sleeper, 0.1)
87 nt.assert_equal(len(jobs.running), 1)
85 nt.assert_equal(len(jobs.running), 1)
88 nt.assert_equal(len(jobs.completed), 0)
86 nt.assert_equal(len(jobs.completed), 0)
89 j.join()
87 j.join()
90 nt.assert_equal(len(jobs.running), 0)
88 nt.assert_equal(len(jobs.running), 0)
91 nt.assert_equal(len(jobs.completed), 1)
89 nt.assert_equal(len(jobs.completed), 1)
@@ -1,53 +1,52 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Test suite for the deepreload module."""
2 """Test suite for the deepreload module."""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Imports
5 # Imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 import os
8 import os
9 import sys
10
9
11 import nose.tools as nt
10 import nose.tools as nt
12
11
13 from IPython.testing import decorators as dec
12 from IPython.testing import decorators as dec
14 from IPython.utils.syspathcontext import prepended_to_syspath
13 from IPython.utils.syspathcontext import prepended_to_syspath
15 from IPython.utils.tempdir import TemporaryDirectory
14 from IPython.utils.tempdir import TemporaryDirectory
16 from IPython.lib.deepreload import reload as dreload
15 from IPython.lib.deepreload import reload as dreload
17
16
18 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
19 # Test functions begin
18 # Test functions begin
20 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
21
20
22 @dec.skipif_not_numpy
21 @dec.skipif_not_numpy
23 def test_deepreload_numpy():
22 def test_deepreload_numpy():
24 "Test that NumPy can be deep reloaded."
23 "Test that NumPy can be deep reloaded."
25 import numpy
24 import numpy
26 exclude = [
25 exclude = [
27 # Standard exclusions:
26 # Standard exclusions:
28 'sys', 'os.path', '__builtin__', '__main__',
27 'sys', 'os.path', '__builtin__', '__main__',
29 # Test-related exclusions:
28 # Test-related exclusions:
30 'unittest', 'UserDict',
29 'unittest', 'UserDict',
31 ]
30 ]
32 dreload(numpy, exclude=exclude)
31 dreload(numpy, exclude=exclude)
33
32
34 def test_deepreload():
33 def test_deepreload():
35 "Test that dreload does deep reloads and skips excluded modules."
34 "Test that dreload does deep reloads and skips excluded modules."
36 with TemporaryDirectory() as tmpdir:
35 with TemporaryDirectory() as tmpdir:
37 with prepended_to_syspath(tmpdir):
36 with prepended_to_syspath(tmpdir):
38 with open(os.path.join(tmpdir, 'A.py'), 'w') as f:
37 with open(os.path.join(tmpdir, 'A.py'), 'w') as f:
39 f.write("class Object(object):\n pass\n")
38 f.write("class Object(object):\n pass\n")
40 with open(os.path.join(tmpdir, 'B.py'), 'w') as f:
39 with open(os.path.join(tmpdir, 'B.py'), 'w') as f:
41 f.write("import A\n")
40 f.write("import A\n")
42 import A
41 import A
43 import B
42 import B
44
43
45 # Test that A is not reloaded.
44 # Test that A is not reloaded.
46 obj = A.Object()
45 obj = A.Object()
47 dreload(B, exclude=['A'])
46 dreload(B, exclude=['A'])
48 nt.assert_true(isinstance(obj, A.Object))
47 nt.assert_true(isinstance(obj, A.Object))
49
48
50 # Test that A is reloaded.
49 # Test that A is reloaded.
51 obj = A.Object()
50 obj = A.Object()
52 dreload(B)
51 dreload(B)
53 nt.assert_false(isinstance(obj, A.Object))
52 nt.assert_false(isinstance(obj, A.Object))
@@ -1,158 +1,157 b''
1 """Tests for IPython.lib.display.
1 """Tests for IPython.lib.display.
2
2
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2012, the IPython Development Team.
5 # Copyright (c) 2012, the IPython Development Team.
6 #
6 #
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 #
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 from __future__ import print_function
15 from __future__ import print_function
16 from tempfile import NamedTemporaryFile, mkdtemp
16 from tempfile import NamedTemporaryFile, mkdtemp
17 from os.path import split
17 from os.path import split
18 from os import sep
19
18
20 # Third-party imports
19 # Third-party imports
21 import nose.tools as nt
20 import nose.tools as nt
22
21
23 # Our own imports
22 # Our own imports
24 from IPython.lib import display
23 from IPython.lib import display
25
24
26 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
27 # Classes and functions
26 # Classes and functions
28 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
29
28
30 #--------------------------
29 #--------------------------
31 # FileLink tests
30 # FileLink tests
32 #--------------------------
31 #--------------------------
33
32
34 def test_instantiation_FileLink():
33 def test_instantiation_FileLink():
35 """FileLink: Test class can be instantiated"""
34 """FileLink: Test class can be instantiated"""
36 fl = display.FileLink('example.txt')
35 fl = display.FileLink('example.txt')
37
36
38 def test_warning_on_non_existant_path_FileLink():
37 def test_warning_on_non_existant_path_FileLink():
39 """FileLink: Calling _repr_html_ on non-existant files returns a warning
38 """FileLink: Calling _repr_html_ on non-existant files returns a warning
40 """
39 """
41 fl = display.FileLink('example.txt')
40 fl = display.FileLink('example.txt')
42 nt.assert_true(fl._repr_html_().startswith('Path (<tt>example.txt</tt>)'))
41 nt.assert_true(fl._repr_html_().startswith('Path (<tt>example.txt</tt>)'))
43
42
44 def test_existing_path_FileLink():
43 def test_existing_path_FileLink():
45 """FileLink: Calling _repr_html_ functions as expected on existing filepath
44 """FileLink: Calling _repr_html_ functions as expected on existing filepath
46 """
45 """
47 tf = NamedTemporaryFile()
46 tf = NamedTemporaryFile()
48 fl = display.FileLink(tf.name)
47 fl = display.FileLink(tf.name)
49 actual = fl._repr_html_()
48 actual = fl._repr_html_()
50 expected = "<a href='files/%s' target='_blank'>%s</a><br>" % (tf.name,tf.name)
49 expected = "<a href='files/%s' target='_blank'>%s</a><br>" % (tf.name,tf.name)
51 nt.assert_equal(actual,expected)
50 nt.assert_equal(actual,expected)
52
51
53 def test_existing_path_FileLink_repr():
52 def test_existing_path_FileLink_repr():
54 """FileLink: Calling repr() functions as expected on existing filepath
53 """FileLink: Calling repr() functions as expected on existing filepath
55 """
54 """
56 tf = NamedTemporaryFile()
55 tf = NamedTemporaryFile()
57 fl = display.FileLink(tf.name)
56 fl = display.FileLink(tf.name)
58 actual = repr(fl)
57 actual = repr(fl)
59 expected = tf.name
58 expected = tf.name
60 nt.assert_equal(actual,expected)
59 nt.assert_equal(actual,expected)
61
60
62 def test_error_on_directory_to_FileLink():
61 def test_error_on_directory_to_FileLink():
63 """FileLink: Raises error when passed directory
62 """FileLink: Raises error when passed directory
64 """
63 """
65 td = mkdtemp()
64 td = mkdtemp()
66 nt.assert_raises(ValueError,display.FileLink,td)
65 nt.assert_raises(ValueError,display.FileLink,td)
67
66
68 #--------------------------
67 #--------------------------
69 # FileLinks tests
68 # FileLinks tests
70 #--------------------------
69 #--------------------------
71
70
72 def test_instantiation_FileLinks():
71 def test_instantiation_FileLinks():
73 """FileLinks: Test class can be instantiated
72 """FileLinks: Test class can be instantiated
74 """
73 """
75 fls = display.FileLinks('example')
74 fls = display.FileLinks('example')
76
75
77 def test_warning_on_non_existant_path_FileLinks():
76 def test_warning_on_non_existant_path_FileLinks():
78 """FileLinks: Calling _repr_html_ on non-existant files returns a warning
77 """FileLinks: Calling _repr_html_ on non-existant files returns a warning
79 """
78 """
80 fls = display.FileLinks('example')
79 fls = display.FileLinks('example')
81 nt.assert_true(fls._repr_html_().startswith('Path (<tt>example</tt>)'))
80 nt.assert_true(fls._repr_html_().startswith('Path (<tt>example</tt>)'))
82
81
83 def test_existing_path_FileLinks():
82 def test_existing_path_FileLinks():
84 """FileLinks: Calling _repr_html_ functions as expected on existing dir
83 """FileLinks: Calling _repr_html_ functions as expected on existing dir
85 """
84 """
86 td = mkdtemp()
85 td = mkdtemp()
87 tf1 = NamedTemporaryFile(dir=td)
86 tf1 = NamedTemporaryFile(dir=td)
88 tf2 = NamedTemporaryFile(dir=td)
87 tf2 = NamedTemporaryFile(dir=td)
89 fl = display.FileLinks(td)
88 fl = display.FileLinks(td)
90 actual = fl._repr_html_()
89 actual = fl._repr_html_()
91 actual = actual.split('\n')
90 actual = actual.split('\n')
92 actual.sort()
91 actual.sort()
93 # the links should always have forward slashes, even on windows, so replace
92 # the links should always have forward slashes, even on windows, so replace
94 # backslashes with forward slashes here
93 # backslashes with forward slashes here
95 expected = ["%s/<br>" % td,
94 expected = ["%s/<br>" % td,
96 "&nbsp;&nbsp;<a href='files/%s' target='_blank'>%s</a><br>" %\
95 "&nbsp;&nbsp;<a href='files/%s' target='_blank'>%s</a><br>" %\
97 (tf2.name.replace("\\","/"),split(tf2.name)[1]),
96 (tf2.name.replace("\\","/"),split(tf2.name)[1]),
98 "&nbsp;&nbsp;<a href='files/%s' target='_blank'>%s</a><br>" %\
97 "&nbsp;&nbsp;<a href='files/%s' target='_blank'>%s</a><br>" %\
99 (tf1.name.replace("\\","/"),split(tf1.name)[1])]
98 (tf1.name.replace("\\","/"),split(tf1.name)[1])]
100 expected.sort()
99 expected.sort()
101 # We compare the sorted list of links here as that's more reliable
100 # We compare the sorted list of links here as that's more reliable
102 nt.assert_equal(actual,expected)
101 nt.assert_equal(actual,expected)
103
102
104 def test_existing_path_FileLinks_alt_formatter():
103 def test_existing_path_FileLinks_alt_formatter():
105 """FileLinks: Calling _repr_html_ functions as expected w/ an alt formatter
104 """FileLinks: Calling _repr_html_ functions as expected w/ an alt formatter
106 """
105 """
107 td = mkdtemp()
106 td = mkdtemp()
108 tf1 = NamedTemporaryFile(dir=td)
107 tf1 = NamedTemporaryFile(dir=td)
109 tf2 = NamedTemporaryFile(dir=td)
108 tf2 = NamedTemporaryFile(dir=td)
110 def fake_formatter(dirname,fnames,included_suffixes):
109 def fake_formatter(dirname,fnames,included_suffixes):
111 return ["hello","world"]
110 return ["hello","world"]
112 fl = display.FileLinks(td,notebook_display_formatter=fake_formatter)
111 fl = display.FileLinks(td,notebook_display_formatter=fake_formatter)
113 actual = fl._repr_html_()
112 actual = fl._repr_html_()
114 actual = actual.split('\n')
113 actual = actual.split('\n')
115 actual.sort()
114 actual.sort()
116 expected = ["hello","world"]
115 expected = ["hello","world"]
117 expected.sort()
116 expected.sort()
118 # We compare the sorted list of links here as that's more reliable
117 # We compare the sorted list of links here as that's more reliable
119 nt.assert_equal(actual,expected)
118 nt.assert_equal(actual,expected)
120
119
121 def test_existing_path_FileLinks_repr():
120 def test_existing_path_FileLinks_repr():
122 """FileLinks: Calling repr() functions as expected on existing directory """
121 """FileLinks: Calling repr() functions as expected on existing directory """
123 td = mkdtemp()
122 td = mkdtemp()
124 tf1 = NamedTemporaryFile(dir=td)
123 tf1 = NamedTemporaryFile(dir=td)
125 tf2 = NamedTemporaryFile(dir=td)
124 tf2 = NamedTemporaryFile(dir=td)
126 fl = display.FileLinks(td)
125 fl = display.FileLinks(td)
127 actual = repr(fl)
126 actual = repr(fl)
128 actual = actual.split('\n')
127 actual = actual.split('\n')
129 actual.sort()
128 actual.sort()
130 expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]]
129 expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]]
131 expected.sort()
130 expected.sort()
132 # We compare the sorted list of links here as that's more reliable
131 # We compare the sorted list of links here as that's more reliable
133 nt.assert_equal(actual,expected)
132 nt.assert_equal(actual,expected)
134
133
135 def test_existing_path_FileLinks_repr_alt_formatter():
134 def test_existing_path_FileLinks_repr_alt_formatter():
136 """FileLinks: Calling repr() functions as expected w/ alt formatter
135 """FileLinks: Calling repr() functions as expected w/ alt formatter
137 """
136 """
138 td = mkdtemp()
137 td = mkdtemp()
139 tf1 = NamedTemporaryFile(dir=td)
138 tf1 = NamedTemporaryFile(dir=td)
140 tf2 = NamedTemporaryFile(dir=td)
139 tf2 = NamedTemporaryFile(dir=td)
141 def fake_formatter(dirname,fnames,included_suffixes):
140 def fake_formatter(dirname,fnames,included_suffixes):
142 return ["hello","world"]
141 return ["hello","world"]
143 fl = display.FileLinks(td,terminal_display_formatter=fake_formatter)
142 fl = display.FileLinks(td,terminal_display_formatter=fake_formatter)
144 actual = repr(fl)
143 actual = repr(fl)
145 actual = actual.split('\n')
144 actual = actual.split('\n')
146 actual.sort()
145 actual.sort()
147 expected = ["hello","world"]
146 expected = ["hello","world"]
148 expected.sort()
147 expected.sort()
149 # We compare the sorted list of links here as that's more reliable
148 # We compare the sorted list of links here as that's more reliable
150 nt.assert_equal(actual,expected)
149 nt.assert_equal(actual,expected)
151
150
152 def test_error_on_file_to_FileLinks():
151 def test_error_on_file_to_FileLinks():
153 """FileLinks: Raises error when passed file
152 """FileLinks: Raises error when passed file
154 """
153 """
155 td = mkdtemp()
154 td = mkdtemp()
156 tf1 = NamedTemporaryFile(dir=td)
155 tf1 = NamedTemporaryFile(dir=td)
157 nt.assert_raises(ValueError,display.FileLinks,tf1.name)
156 nt.assert_raises(ValueError,display.FileLinks,tf1.name)
158
157
@@ -1,13 +1,13 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 def test_import_backgroundjobs():
3 def test_import_backgroundjobs():
4 from IPython.lib import backgroundjobs
4 from IPython.lib import backgroundjobs
5
5
6 def test_import_deepreload():
6 def test_import_deepreload():
7 from IPython.lib import deepreload
7 from IPython.lib import deepreload
8
8
9 def test_import_demo():
9 def test_import_demo():
10 from IPython.lib import demo
10 from IPython.lib import demo
11
11
12 def test_import_irunner():
12 def test_import_irunner():
13 from IPython.lib import demo
13 from IPython.lib import irunner
General Comments 0
You need to be logged in to leave comments. Login now