##// END OF EJS Templates
Add stacklevel to deprecation warning and filter accordingly
Matthias Bussonnier -
Show More
@@ -1,664 +1,666 b''
1 # coding: utf-8
1 # coding: utf-8
2 """
2 """
3 Deprecated since IPython 5.0
3 Deprecated since IPython 5.0
4
4
5 Inputhook management for GUI event loop integration.
5 Inputhook management for GUI event loop integration.
6 """
6 """
7
7
8 # Copyright (c) IPython Development Team.
8 # Copyright (c) IPython Development Team.
9 # Distributed under the terms of the Modified BSD License.
9 # Distributed under the terms of the Modified BSD License.
10
10
11 try:
11 try:
12 import ctypes
12 import ctypes
13 except ImportError:
13 except ImportError:
14 ctypes = None
14 ctypes = None
15 except SystemError: # IronPython issue, 2/8/2014
15 except SystemError: # IronPython issue, 2/8/2014
16 ctypes = None
16 ctypes = None
17 import os
17 import os
18 import platform
18 import platform
19 import sys
19 import sys
20 from distutils.version import LooseVersion as V
20 from distutils.version import LooseVersion as V
21
21
22 from warnings import warn
22 from warnings import warn
23
23
24
24
25 warn("`IPython.lib.inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
25 warn("`IPython.lib.inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
26 DeprecationWarning, stacklevel=2)
26 DeprecationWarning, stacklevel=2)
27
27
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Constants
30 # Constants
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 # Constants for identifying the GUI toolkits.
33 # Constants for identifying the GUI toolkits.
34 GUI_WX = 'wx'
34 GUI_WX = 'wx'
35 GUI_QT = 'qt'
35 GUI_QT = 'qt'
36 GUI_QT4 = 'qt4'
36 GUI_QT4 = 'qt4'
37 GUI_GTK = 'gtk'
37 GUI_GTK = 'gtk'
38 GUI_TK = 'tk'
38 GUI_TK = 'tk'
39 GUI_OSX = 'osx'
39 GUI_OSX = 'osx'
40 GUI_GLUT = 'glut'
40 GUI_GLUT = 'glut'
41 GUI_PYGLET = 'pyglet'
41 GUI_PYGLET = 'pyglet'
42 GUI_GTK3 = 'gtk3'
42 GUI_GTK3 = 'gtk3'
43 GUI_NONE = 'none' # i.e. disable
43 GUI_NONE = 'none' # i.e. disable
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Utilities
46 # Utilities
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49 def _stdin_ready_posix():
49 def _stdin_ready_posix():
50 """Return True if there's something to read on stdin (posix version)."""
50 """Return True if there's something to read on stdin (posix version)."""
51 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
51 infds, outfds, erfds = select.select([sys.stdin],[],[],0)
52 return bool(infds)
52 return bool(infds)
53
53
54 def _stdin_ready_nt():
54 def _stdin_ready_nt():
55 """Return True if there's something to read on stdin (nt version)."""
55 """Return True if there's something to read on stdin (nt version)."""
56 return msvcrt.kbhit()
56 return msvcrt.kbhit()
57
57
58 def _stdin_ready_other():
58 def _stdin_ready_other():
59 """Return True, assuming there's something to read on stdin."""
59 """Return True, assuming there's something to read on stdin."""
60 return True
60 return True
61
61
62 def _use_appnope():
62 def _use_appnope():
63 """Should we use appnope for dealing with OS X app nap?
63 """Should we use appnope for dealing with OS X app nap?
64
64
65 Checks if we are on OS X 10.9 or greater.
65 Checks if we are on OS X 10.9 or greater.
66 """
66 """
67 return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9')
67 return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9')
68
68
69 def _ignore_CTRL_C_posix():
69 def _ignore_CTRL_C_posix():
70 """Ignore CTRL+C (SIGINT)."""
70 """Ignore CTRL+C (SIGINT)."""
71 signal.signal(signal.SIGINT, signal.SIG_IGN)
71 signal.signal(signal.SIGINT, signal.SIG_IGN)
72
72
73 def _allow_CTRL_C_posix():
73 def _allow_CTRL_C_posix():
74 """Take CTRL+C into account (SIGINT)."""
74 """Take CTRL+C into account (SIGINT)."""
75 signal.signal(signal.SIGINT, signal.default_int_handler)
75 signal.signal(signal.SIGINT, signal.default_int_handler)
76
76
77 def _ignore_CTRL_C_other():
77 def _ignore_CTRL_C_other():
78 """Ignore CTRL+C (not implemented)."""
78 """Ignore CTRL+C (not implemented)."""
79 pass
79 pass
80
80
81 def _allow_CTRL_C_other():
81 def _allow_CTRL_C_other():
82 """Take CTRL+C into account (not implemented)."""
82 """Take CTRL+C into account (not implemented)."""
83 pass
83 pass
84
84
85 if os.name == 'posix':
85 if os.name == 'posix':
86 import select
86 import select
87 import signal
87 import signal
88 stdin_ready = _stdin_ready_posix
88 stdin_ready = _stdin_ready_posix
89 ignore_CTRL_C = _ignore_CTRL_C_posix
89 ignore_CTRL_C = _ignore_CTRL_C_posix
90 allow_CTRL_C = _allow_CTRL_C_posix
90 allow_CTRL_C = _allow_CTRL_C_posix
91 elif os.name == 'nt':
91 elif os.name == 'nt':
92 import msvcrt
92 import msvcrt
93 stdin_ready = _stdin_ready_nt
93 stdin_ready = _stdin_ready_nt
94 ignore_CTRL_C = _ignore_CTRL_C_other
94 ignore_CTRL_C = _ignore_CTRL_C_other
95 allow_CTRL_C = _allow_CTRL_C_other
95 allow_CTRL_C = _allow_CTRL_C_other
96 else:
96 else:
97 stdin_ready = _stdin_ready_other
97 stdin_ready = _stdin_ready_other
98 ignore_CTRL_C = _ignore_CTRL_C_other
98 ignore_CTRL_C = _ignore_CTRL_C_other
99 allow_CTRL_C = _allow_CTRL_C_other
99 allow_CTRL_C = _allow_CTRL_C_other
100
100
101
101
102 #-----------------------------------------------------------------------------
102 #-----------------------------------------------------------------------------
103 # Main InputHookManager class
103 # Main InputHookManager class
104 #-----------------------------------------------------------------------------
104 #-----------------------------------------------------------------------------
105
105
106
106
107 class InputHookManager(object):
107 class InputHookManager(object):
108 """DEPRECATED since IPython 5.0
108 """DEPRECATED since IPython 5.0
109
109
110 Manage PyOS_InputHook for different GUI toolkits.
110 Manage PyOS_InputHook for different GUI toolkits.
111
111
112 This class installs various hooks under ``PyOSInputHook`` to handle
112 This class installs various hooks under ``PyOSInputHook`` to handle
113 GUI event loop integration.
113 GUI event loop integration.
114 """
114 """
115
115
116 def __init__(self):
116 def __init__(self):
117 if ctypes is None:
117 if ctypes is None:
118 warn("IPython GUI event loop requires ctypes, %gui will not be available")
118 warn("IPython GUI event loop requires ctypes, %gui will not be available")
119 else:
119 else:
120 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
120 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
121 self.guihooks = {}
121 self.guihooks = {}
122 self.aliases = {}
122 self.aliases = {}
123 self.apps = {}
123 self.apps = {}
124 self._reset()
124 self._reset()
125
125
126 def _reset(self):
126 def _reset(self):
127 self._callback_pyfunctype = None
127 self._callback_pyfunctype = None
128 self._callback = None
128 self._callback = None
129 self._installed = False
129 self._installed = False
130 self._current_gui = None
130 self._current_gui = None
131
131
132 def get_pyos_inputhook(self):
132 def get_pyos_inputhook(self):
133 """DEPRECATED since IPython 5.0
133 """DEPRECATED since IPython 5.0
134
134
135 Return the current PyOS_InputHook as a ctypes.c_void_p."""
135 Return the current PyOS_InputHook as a ctypes.c_void_p."""
136 warn("`get_pyos_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
136 warn("`get_pyos_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
137 DeprecationWarning, stacklevel=2)
137 DeprecationWarning, stacklevel=2)
138 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
138 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
139
139
140 def get_pyos_inputhook_as_func(self):
140 def get_pyos_inputhook_as_func(self):
141 """DEPRECATED since IPython 5.0
141 """DEPRECATED since IPython 5.0
142
142
143 Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
143 Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
144 warn("`get_pyos_inputhook_as_func` is deprecated since IPython 5.0 and will be removed in future versions.",
144 warn("`get_pyos_inputhook_as_func` is deprecated since IPython 5.0 and will be removed in future versions.",
145 DeprecationWarning, stacklevel=2)
145 DeprecationWarning, stacklevel=2)
146 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
146 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
147
147
148 def set_inputhook(self, callback):
148 def set_inputhook(self, callback):
149 """DEPRECATED since IPython 5.0
149 """DEPRECATED since IPython 5.0
150
150
151 Set PyOS_InputHook to callback and return the previous one."""
151 Set PyOS_InputHook to callback and return the previous one."""
152 # On platforms with 'readline' support, it's all too likely to
152 # On platforms with 'readline' support, it's all too likely to
153 # have a KeyboardInterrupt signal delivered *even before* an
153 # have a KeyboardInterrupt signal delivered *even before* an
154 # initial ``try:`` clause in the callback can be executed, so
154 # initial ``try:`` clause in the callback can be executed, so
155 # we need to disable CTRL+C in this situation.
155 # we need to disable CTRL+C in this situation.
156 ignore_CTRL_C()
156 ignore_CTRL_C()
157 self._callback = callback
157 self._callback = callback
158 self._callback_pyfunctype = self.PYFUNC(callback)
158 self._callback_pyfunctype = self.PYFUNC(callback)
159 pyos_inputhook_ptr = self.get_pyos_inputhook()
159 pyos_inputhook_ptr = self.get_pyos_inputhook()
160 original = self.get_pyos_inputhook_as_func()
160 original = self.get_pyos_inputhook_as_func()
161 pyos_inputhook_ptr.value = \
161 pyos_inputhook_ptr.value = \
162 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
162 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
163 self._installed = True
163 self._installed = True
164 return original
164 return original
165
165
166 def clear_inputhook(self, app=None):
166 def clear_inputhook(self, app=None):
167 """DEPRECATED since IPython 5.0
167 """DEPRECATED since IPython 5.0
168
168
169 Set PyOS_InputHook to NULL and return the previous one.
169 Set PyOS_InputHook to NULL and return the previous one.
170
170
171 Parameters
171 Parameters
172 ----------
172 ----------
173 app : optional, ignored
173 app : optional, ignored
174 This parameter is allowed only so that clear_inputhook() can be
174 This parameter is allowed only so that clear_inputhook() can be
175 called with a similar interface as all the ``enable_*`` methods. But
175 called with a similar interface as all the ``enable_*`` methods. But
176 the actual value of the parameter is ignored. This uniform interface
176 the actual value of the parameter is ignored. This uniform interface
177 makes it easier to have user-level entry points in the main IPython
177 makes it easier to have user-level entry points in the main IPython
178 app like :meth:`enable_gui`."""
178 app like :meth:`enable_gui`."""
179 warn("`clear_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
179 warn("`clear_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.",
180 DeprecationWarning, stacklevel=2)
180 DeprecationWarning, stacklevel=2)
181 pyos_inputhook_ptr = self.get_pyos_inputhook()
181 pyos_inputhook_ptr = self.get_pyos_inputhook()
182 original = self.get_pyos_inputhook_as_func()
182 original = self.get_pyos_inputhook_as_func()
183 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
183 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
184 allow_CTRL_C()
184 allow_CTRL_C()
185 self._reset()
185 self._reset()
186 return original
186 return original
187
187
188 def clear_app_refs(self, gui=None):
188 def clear_app_refs(self, gui=None):
189 """DEPRECATED since IPython 5.0
189 """DEPRECATED since IPython 5.0
190
190
191 Clear IPython's internal reference to an application instance.
191 Clear IPython's internal reference to an application instance.
192
192
193 Whenever we create an app for a user on qt4 or wx, we hold a
193 Whenever we create an app for a user on qt4 or wx, we hold a
194 reference to the app. This is needed because in some cases bad things
194 reference to the app. This is needed because in some cases bad things
195 can happen if a user doesn't hold a reference themselves. This
195 can happen if a user doesn't hold a reference themselves. This
196 method is provided to clear the references we are holding.
196 method is provided to clear the references we are holding.
197
197
198 Parameters
198 Parameters
199 ----------
199 ----------
200 gui : None or str
200 gui : None or str
201 If None, clear all app references. If ('wx', 'qt4') clear
201 If None, clear all app references. If ('wx', 'qt4') clear
202 the app for that toolkit. References are not held for gtk or tk
202 the app for that toolkit. References are not held for gtk or tk
203 as those toolkits don't have the notion of an app.
203 as those toolkits don't have the notion of an app.
204 """
204 """
205 warn("`clear_app_refs` is deprecated since IPython 5.0 and will be removed in future versions.",
205 warn("`clear_app_refs` is deprecated since IPython 5.0 and will be removed in future versions.",
206 DeprecationWarning, stacklevel=2)
206 DeprecationWarning, stacklevel=2)
207 if gui is None:
207 if gui is None:
208 self.apps = {}
208 self.apps = {}
209 elif gui in self.apps:
209 elif gui in self.apps:
210 del self.apps[gui]
210 del self.apps[gui]
211
211
212 def register(self, toolkitname, *aliases):
212 def register(self, toolkitname, *aliases):
213 """DEPRECATED since IPython 5.0
213 """DEPRECATED since IPython 5.0
214
214
215 Register a class to provide the event loop for a given GUI.
215 Register a class to provide the event loop for a given GUI.
216
216
217 This is intended to be used as a class decorator. It should be passed
217 This is intended to be used as a class decorator. It should be passed
218 the names with which to register this GUI integration. The classes
218 the names with which to register this GUI integration. The classes
219 themselves should subclass :class:`InputHookBase`.
219 themselves should subclass :class:`InputHookBase`.
220
220
221 ::
221 ::
222
222
223 @inputhook_manager.register('qt')
223 @inputhook_manager.register('qt')
224 class QtInputHook(InputHookBase):
224 class QtInputHook(InputHookBase):
225 def enable(self, app=None):
225 def enable(self, app=None):
226 ...
226 ...
227 """
227 """
228 warn("`register` is deprecated since IPython 5.0 and will be removed in future versions.",
228 warn("`register` is deprecated since IPython 5.0 and will be removed in future versions.",
229 DeprecationWarning, stacklevel=2)
229 DeprecationWarning, stacklevel=2)
230 def decorator(cls):
230 def decorator(cls):
231 if ctypes is not None:
231 if ctypes is not None:
232 inst = cls(self)
232 inst = cls(self)
233 self.guihooks[toolkitname] = inst
233 self.guihooks[toolkitname] = inst
234 for a in aliases:
234 for a in aliases:
235 self.aliases[a] = toolkitname
235 self.aliases[a] = toolkitname
236 return cls
236 return cls
237 return decorator
237 return decorator
238
238
239 def current_gui(self):
239 def current_gui(self):
240 """DEPRECATED since IPython 5.0
240 """DEPRECATED since IPython 5.0
241
241
242 Return a string indicating the currently active GUI or None."""
242 Return a string indicating the currently active GUI or None."""
243 warn("`current_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
243 warn("`current_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
244 DeprecationWarning, stacklevel=2)
244 DeprecationWarning, stacklevel=2)
245 return self._current_gui
245 return self._current_gui
246
246
247 def enable_gui(self, gui=None, app=None):
247 def enable_gui(self, gui=None, app=None):
248 """DEPRECATED since IPython 5.0
248 """DEPRECATED since IPython 5.0
249
249
250 Switch amongst GUI input hooks by name.
250 Switch amongst GUI input hooks by name.
251
251
252 This is a higher level method than :meth:`set_inputhook` - it uses the
252 This is a higher level method than :meth:`set_inputhook` - it uses the
253 GUI name to look up a registered object which enables the input hook
253 GUI name to look up a registered object which enables the input hook
254 for that GUI.
254 for that GUI.
255
255
256 Parameters
256 Parameters
257 ----------
257 ----------
258 gui : optional, string or None
258 gui : optional, string or None
259 If None (or 'none'), clears input hook, otherwise it must be one
259 If None (or 'none'), clears input hook, otherwise it must be one
260 of the recognized GUI names (see ``GUI_*`` constants in module).
260 of the recognized GUI names (see ``GUI_*`` constants in module).
261
261
262 app : optional, existing application object.
262 app : optional, existing application object.
263 For toolkits that have the concept of a global app, you can supply an
263 For toolkits that have the concept of a global app, you can supply an
264 existing one. If not given, the toolkit will be probed for one, and if
264 existing one. If not given, the toolkit will be probed for one, and if
265 none is found, a new one will be created. Note that GTK does not have
265 none is found, a new one will be created. Note that GTK does not have
266 this concept, and passing an app if ``gui=="GTK"`` will raise an error.
266 this concept, and passing an app if ``gui=="GTK"`` will raise an error.
267
267
268 Returns
268 Returns
269 -------
269 -------
270 The output of the underlying gui switch routine, typically the actual
270 The output of the underlying gui switch routine, typically the actual
271 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
271 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
272 one.
272 one.
273 """
273 """
274 warn("`enable_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
274 warn("`enable_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
275 DeprecationWarning, stacklevel=2)
275 DeprecationWarning, stacklevel=2)
276 if gui in (None, GUI_NONE):
276 if gui in (None, GUI_NONE):
277 return self.disable_gui()
277 return self.disable_gui()
278
278
279 if gui in self.aliases:
279 if gui in self.aliases:
280 return self.enable_gui(self.aliases[gui], app)
280 return self.enable_gui(self.aliases[gui], app)
281
281
282 try:
282 try:
283 gui_hook = self.guihooks[gui]
283 gui_hook = self.guihooks[gui]
284 except KeyError:
284 except KeyError:
285 e = "Invalid GUI request {!r}, valid ones are: {}"
285 e = "Invalid GUI request {!r}, valid ones are: {}"
286 raise ValueError(e.format(gui, ', '.join(self.guihooks)))
286 raise ValueError(e.format(gui, ', '.join(self.guihooks)))
287 self._current_gui = gui
287 self._current_gui = gui
288
288
289 app = gui_hook.enable(app)
289 app = gui_hook.enable(app)
290 if app is not None:
290 if app is not None:
291 app._in_event_loop = True
291 app._in_event_loop = True
292 self.apps[gui] = app
292 self.apps[gui] = app
293 return app
293 return app
294
294
295 def disable_gui(self):
295 def disable_gui(self):
296 """DEPRECATED since IPython 5.0
296 """DEPRECATED since IPython 5.0
297
297
298 Disable GUI event loop integration.
298 Disable GUI event loop integration.
299
299
300 If an application was registered, this sets its ``_in_event_loop``
300 If an application was registered, this sets its ``_in_event_loop``
301 attribute to False. It then calls :meth:`clear_inputhook`.
301 attribute to False. It then calls :meth:`clear_inputhook`.
302 """
302 """
303 warn("`disable_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
303 warn("`disable_gui` is deprecated since IPython 5.0 and will be removed in future versions.",
304 DeprecationWarning, stacklevel=2)
304 DeprecationWarning, stacklevel=2)
305 gui = self._current_gui
305 gui = self._current_gui
306 if gui in self.apps:
306 if gui in self.apps:
307 self.apps[gui]._in_event_loop = False
307 self.apps[gui]._in_event_loop = False
308 return self.clear_inputhook()
308 return self.clear_inputhook()
309
309
310 class InputHookBase(object):
310 class InputHookBase(object):
311 """DEPRECATED since IPython 5.0
311 """DEPRECATED since IPython 5.0
312
312
313 Base class for input hooks for specific toolkits.
313 Base class for input hooks for specific toolkits.
314
314
315 Subclasses should define an :meth:`enable` method with one argument, ``app``,
315 Subclasses should define an :meth:`enable` method with one argument, ``app``,
316 which will either be an instance of the toolkit's application class, or None.
316 which will either be an instance of the toolkit's application class, or None.
317 They may also define a :meth:`disable` method with no arguments.
317 They may also define a :meth:`disable` method with no arguments.
318 """
318 """
319 def __init__(self, manager):
319 def __init__(self, manager):
320 self.manager = manager
320 self.manager = manager
321
321
322 def disable(self):
322 def disable(self):
323 pass
323 pass
324
324
325 inputhook_manager = InputHookManager()
325 inputhook_manager = InputHookManager()
326
326
327 @inputhook_manager.register('osx')
327 @inputhook_manager.register('osx')
328 class NullInputHook(InputHookBase):
328 class NullInputHook(InputHookBase):
329 """DEPRECATED since IPython 5.0
329 """DEPRECATED since IPython 5.0
330
330
331 A null inputhook that doesn't need to do anything"""
331 A null inputhook that doesn't need to do anything"""
332 def enable(self, app=None):
332 def enable(self, app=None):
333 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
333 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
334 DeprecationWarning, stacklevel=2)
334 DeprecationWarning, stacklevel=2)
335
335
336 @inputhook_manager.register('wx')
336 @inputhook_manager.register('wx')
337 class WxInputHook(InputHookBase):
337 class WxInputHook(InputHookBase):
338 def enable(self, app=None):
338 def enable(self, app=None):
339 """DEPRECATED since IPython 5.0
339 """DEPRECATED since IPython 5.0
340
340
341 Enable event loop integration with wxPython.
341 Enable event loop integration with wxPython.
342
342
343 Parameters
343 Parameters
344 ----------
344 ----------
345 app : WX Application, optional.
345 app : WX Application, optional.
346 Running application to use. If not given, we probe WX for an
346 Running application to use. If not given, we probe WX for an
347 existing application object, and create a new one if none is found.
347 existing application object, and create a new one if none is found.
348
348
349 Notes
349 Notes
350 -----
350 -----
351 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
351 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
352 the wxPython to integrate with terminal based applications like
352 the wxPython to integrate with terminal based applications like
353 IPython.
353 IPython.
354
354
355 If ``app`` is not given we probe for an existing one, and return it if
355 If ``app`` is not given we probe for an existing one, and return it if
356 found. If no existing app is found, we create an :class:`wx.App` as
356 found. If no existing app is found, we create an :class:`wx.App` as
357 follows::
357 follows::
358
358
359 import wx
359 import wx
360 app = wx.App(redirect=False, clearSigInt=False)
360 app = wx.App(redirect=False, clearSigInt=False)
361 """
361 """
362 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
362 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
363 DeprecationWarning, stacklevel=2)
363 DeprecationWarning, stacklevel=2)
364 import wx
364 import wx
365
365
366 wx_version = V(wx.__version__).version
366 wx_version = V(wx.__version__).version
367
367
368 if wx_version < [2, 8]:
368 if wx_version < [2, 8]:
369 raise ValueError("requires wxPython >= 2.8, but you have %s" % wx.__version__)
369 raise ValueError("requires wxPython >= 2.8, but you have %s" % wx.__version__)
370
370
371 from IPython.lib.inputhookwx import inputhook_wx
371 from IPython.lib.inputhookwx import inputhook_wx
372 self.manager.set_inputhook(inputhook_wx)
372 self.manager.set_inputhook(inputhook_wx)
373 if _use_appnope():
373 if _use_appnope():
374 from appnope import nope
374 from appnope import nope
375 nope()
375 nope()
376
376
377 import wx
377 import wx
378 if app is None:
378 if app is None:
379 app = wx.GetApp()
379 app = wx.GetApp()
380 if app is None:
380 if app is None:
381 app = wx.App(redirect=False, clearSigInt=False)
381 app = wx.App(redirect=False, clearSigInt=False)
382
382
383 return app
383 return app
384
384
385 def disable(self):
385 def disable(self):
386 """DEPRECATED since IPython 5.0
386 """DEPRECATED since IPython 5.0
387
387
388 Disable event loop integration with wxPython.
388 Disable event loop integration with wxPython.
389
389
390 This restores appnapp on OS X
390 This restores appnapp on OS X
391 """
391 """
392 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
392 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
393 DeprecationWarning, stacklevel=2)
393 DeprecationWarning, stacklevel=2)
394 if _use_appnope():
394 if _use_appnope():
395 from appnope import nap
395 from appnope import nap
396 nap()
396 nap()
397
397
398 @inputhook_manager.register('qt', 'qt4')
398 @inputhook_manager.register('qt', 'qt4')
399 class Qt4InputHook(InputHookBase):
399 class Qt4InputHook(InputHookBase):
400 def enable(self, app=None):
400 def enable(self, app=None):
401 """DEPRECATED since IPython 5.0
401 """DEPRECATED since IPython 5.0
402
402
403 Enable event loop integration with PyQt4.
403 Enable event loop integration with PyQt4.
404
404
405 Parameters
405 Parameters
406 ----------
406 ----------
407 app : Qt Application, optional.
407 app : Qt Application, optional.
408 Running application to use. If not given, we probe Qt for an
408 Running application to use. If not given, we probe Qt for an
409 existing application object, and create a new one if none is found.
409 existing application object, and create a new one if none is found.
410
410
411 Notes
411 Notes
412 -----
412 -----
413 This methods sets the PyOS_InputHook for PyQt4, which allows
413 This methods sets the PyOS_InputHook for PyQt4, which allows
414 the PyQt4 to integrate with terminal based applications like
414 the PyQt4 to integrate with terminal based applications like
415 IPython.
415 IPython.
416
416
417 If ``app`` is not given we probe for an existing one, and return it if
417 If ``app`` is not given we probe for an existing one, and return it if
418 found. If no existing app is found, we create an :class:`QApplication`
418 found. If no existing app is found, we create an :class:`QApplication`
419 as follows::
419 as follows::
420
420
421 from PyQt4 import QtCore
421 from PyQt4 import QtCore
422 app = QtGui.QApplication(sys.argv)
422 app = QtGui.QApplication(sys.argv)
423 """
423 """
424 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
424 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
425 DeprecationWarning, stacklevel=2)
425 DeprecationWarning, stacklevel=2)
426 from IPython.lib.inputhookqt4 import create_inputhook_qt4
426 from IPython.lib.inputhookqt4 import create_inputhook_qt4
427 app, inputhook_qt4 = create_inputhook_qt4(self.manager, app)
427 app, inputhook_qt4 = create_inputhook_qt4(self.manager, app)
428 self.manager.set_inputhook(inputhook_qt4)
428 self.manager.set_inputhook(inputhook_qt4)
429 if _use_appnope():
429 if _use_appnope():
430 from appnope import nope
430 from appnope import nope
431 nope()
431 nope()
432
432
433 return app
433 return app
434
434
435 def disable_qt4(self):
435 def disable_qt4(self):
436 """DEPRECATED since IPython 5.0
436 """DEPRECATED since IPython 5.0
437
437
438 Disable event loop integration with PyQt4.
438 Disable event loop integration with PyQt4.
439
439
440 This restores appnapp on OS X
440 This restores appnapp on OS X
441 """
441 """
442 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
442 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
443 DeprecationWarning, stacklevel=2)
443 DeprecationWarning, stacklevel=2)
444 if _use_appnope():
444 if _use_appnope():
445 from appnope import nap
445 from appnope import nap
446 nap()
446 nap()
447
447
448
448
449 @inputhook_manager.register('qt5')
449 @inputhook_manager.register('qt5')
450 class Qt5InputHook(Qt4InputHook):
450 class Qt5InputHook(Qt4InputHook):
451 def enable(self, app=None):
451 def enable(self, app=None):
452 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
452 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
453 DeprecationWarning, stacklevel=2)
453 DeprecationWarning, stacklevel=2)
454 os.environ['QT_API'] = 'pyqt5'
454 os.environ['QT_API'] = 'pyqt5'
455 return Qt4InputHook.enable(self, app)
455 return Qt4InputHook.enable(self, app)
456
456
457
457
458 @inputhook_manager.register('gtk')
458 @inputhook_manager.register('gtk')
459 class GtkInputHook(InputHookBase):
459 class GtkInputHook(InputHookBase):
460 def enable(self, app=None):
460 def enable(self, app=None):
461 """DEPRECATED since IPython 5.0
461 """DEPRECATED since IPython 5.0
462
462
463 Enable event loop integration with PyGTK.
463 Enable event loop integration with PyGTK.
464
464
465 Parameters
465 Parameters
466 ----------
466 ----------
467 app : ignored
467 app : ignored
468 Ignored, it's only a placeholder to keep the call signature of all
468 Ignored, it's only a placeholder to keep the call signature of all
469 gui activation methods consistent, which simplifies the logic of
469 gui activation methods consistent, which simplifies the logic of
470 supporting magics.
470 supporting magics.
471
471
472 Notes
472 Notes
473 -----
473 -----
474 This methods sets the PyOS_InputHook for PyGTK, which allows
474 This methods sets the PyOS_InputHook for PyGTK, which allows
475 the PyGTK to integrate with terminal based applications like
475 the PyGTK to integrate with terminal based applications like
476 IPython.
476 IPython.
477 """
477 """
478 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
478 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
479 DeprecationWarning, stacklevel=2)
479 DeprecationWarning, stacklevel=2)
480 import gtk
480 import gtk
481 try:
481 try:
482 gtk.set_interactive(True)
482 gtk.set_interactive(True)
483 except AttributeError:
483 except AttributeError:
484 # For older versions of gtk, use our own ctypes version
484 # For older versions of gtk, use our own ctypes version
485 from IPython.lib.inputhookgtk import inputhook_gtk
485 from IPython.lib.inputhookgtk import inputhook_gtk
486 self.manager.set_inputhook(inputhook_gtk)
486 self.manager.set_inputhook(inputhook_gtk)
487
487
488
488
489 @inputhook_manager.register('tk')
489 @inputhook_manager.register('tk')
490 class TkInputHook(InputHookBase):
490 class TkInputHook(InputHookBase):
491 def enable(self, app=None):
491 def enable(self, app=None):
492 """DEPRECATED since IPython 5.0
492 """DEPRECATED since IPython 5.0
493
493
494 Enable event loop integration with Tk.
494 Enable event loop integration with Tk.
495
495
496 Parameters
496 Parameters
497 ----------
497 ----------
498 app : toplevel :class:`Tkinter.Tk` widget, optional.
498 app : toplevel :class:`Tkinter.Tk` widget, optional.
499 Running toplevel widget to use. If not given, we probe Tk for an
499 Running toplevel widget to use. If not given, we probe Tk for an
500 existing one, and create a new one if none is found.
500 existing one, and create a new one if none is found.
501
501
502 Notes
502 Notes
503 -----
503 -----
504 If you have already created a :class:`Tkinter.Tk` object, the only
504 If you have already created a :class:`Tkinter.Tk` object, the only
505 thing done by this method is to register with the
505 thing done by this method is to register with the
506 :class:`InputHookManager`, since creating that object automatically
506 :class:`InputHookManager`, since creating that object automatically
507 sets ``PyOS_InputHook``.
507 sets ``PyOS_InputHook``.
508 """
508 """
509 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
509 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
510 DeprecationWarning, stacklevel=2)
510 DeprecationWarning, stacklevel=2)
511 if app is None:
511 if app is None:
512 try:
512 try:
513 from tkinter import Tk # Py 3
513 from tkinter import Tk # Py 3
514 except ImportError:
514 except ImportError:
515 from Tkinter import Tk # Py 2
515 from Tkinter import Tk # Py 2
516 app = Tk()
516 app = Tk()
517 app.withdraw()
517 app.withdraw()
518 self.manager.apps[GUI_TK] = app
518 self.manager.apps[GUI_TK] = app
519 return app
519 return app
520
520
521
521
522 @inputhook_manager.register('glut')
522 @inputhook_manager.register('glut')
523 class GlutInputHook(InputHookBase):
523 class GlutInputHook(InputHookBase):
524 def enable(self, app=None):
524 def enable(self, app=None):
525 """DEPRECATED since IPython 5.0
525 """DEPRECATED since IPython 5.0
526
526
527 Enable event loop integration with GLUT.
527 Enable event loop integration with GLUT.
528
528
529 Parameters
529 Parameters
530 ----------
530 ----------
531
531
532 app : ignored
532 app : ignored
533 Ignored, it's only a placeholder to keep the call signature of all
533 Ignored, it's only a placeholder to keep the call signature of all
534 gui activation methods consistent, which simplifies the logic of
534 gui activation methods consistent, which simplifies the logic of
535 supporting magics.
535 supporting magics.
536
536
537 Notes
537 Notes
538 -----
538 -----
539
539
540 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
540 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
541 integrate with terminal based applications like IPython. Due to GLUT
541 integrate with terminal based applications like IPython. Due to GLUT
542 limitations, it is currently not possible to start the event loop
542 limitations, it is currently not possible to start the event loop
543 without first creating a window. You should thus not create another
543 without first creating a window. You should thus not create another
544 window but use instead the created one. See 'gui-glut.py' in the
544 window but use instead the created one. See 'gui-glut.py' in the
545 docs/examples/lib directory.
545 docs/examples/lib directory.
546
546
547 The default screen mode is set to:
547 The default screen mode is set to:
548 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
548 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
549 """
549 """
550 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
550 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
551 DeprecationWarning, stacklevel=2)
551 DeprecationWarning, stacklevel=2)
552
552
553 import OpenGL.GLUT as glut
553 import OpenGL.GLUT as glut
554 from IPython.lib.inputhookglut import glut_display_mode, \
554 from IPython.lib.inputhookglut import glut_display_mode, \
555 glut_close, glut_display, \
555 glut_close, glut_display, \
556 glut_idle, inputhook_glut
556 glut_idle, inputhook_glut
557
557
558 if GUI_GLUT not in self.manager.apps:
558 if GUI_GLUT not in self.manager.apps:
559 glut.glutInit( sys.argv )
559 glut.glutInit( sys.argv )
560 glut.glutInitDisplayMode( glut_display_mode )
560 glut.glutInitDisplayMode( glut_display_mode )
561 # This is specific to freeglut
561 # This is specific to freeglut
562 if bool(glut.glutSetOption):
562 if bool(glut.glutSetOption):
563 glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
563 glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
564 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
564 glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
565 glut.glutCreateWindow( sys.argv[0] )
565 glut.glutCreateWindow( sys.argv[0] )
566 glut.glutReshapeWindow( 1, 1 )
566 glut.glutReshapeWindow( 1, 1 )
567 glut.glutHideWindow( )
567 glut.glutHideWindow( )
568 glut.glutWMCloseFunc( glut_close )
568 glut.glutWMCloseFunc( glut_close )
569 glut.glutDisplayFunc( glut_display )
569 glut.glutDisplayFunc( glut_display )
570 glut.glutIdleFunc( glut_idle )
570 glut.glutIdleFunc( glut_idle )
571 else:
571 else:
572 glut.glutWMCloseFunc( glut_close )
572 glut.glutWMCloseFunc( glut_close )
573 glut.glutDisplayFunc( glut_display )
573 glut.glutDisplayFunc( glut_display )
574 glut.glutIdleFunc( glut_idle)
574 glut.glutIdleFunc( glut_idle)
575 self.manager.set_inputhook( inputhook_glut )
575 self.manager.set_inputhook( inputhook_glut )
576
576
577
577
578 def disable(self):
578 def disable(self):
579 """DEPRECATED since IPython 5.0
579 """DEPRECATED since IPython 5.0
580
580
581 Disable event loop integration with glut.
581 Disable event loop integration with glut.
582
582
583 This sets PyOS_InputHook to NULL and set the display function to a
583 This sets PyOS_InputHook to NULL and set the display function to a
584 dummy one and set the timer to a dummy timer that will be triggered
584 dummy one and set the timer to a dummy timer that will be triggered
585 very far in the future.
585 very far in the future.
586 """
586 """
587 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
587 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
588 DeprecationWarning, stacklevel=2)
588 DeprecationWarning, stacklevel=2)
589 import OpenGL.GLUT as glut
589 import OpenGL.GLUT as glut
590 from glut_support import glutMainLoopEvent
590 from glut_support import glutMainLoopEvent
591
591
592 glut.glutHideWindow() # This is an event to be processed below
592 glut.glutHideWindow() # This is an event to be processed below
593 glutMainLoopEvent()
593 glutMainLoopEvent()
594 super(GlutInputHook, self).disable()
594 super(GlutInputHook, self).disable()
595
595
596 @inputhook_manager.register('pyglet')
596 @inputhook_manager.register('pyglet')
597 class PygletInputHook(InputHookBase):
597 class PygletInputHook(InputHookBase):
598 def enable(self, app=None):
598 def enable(self, app=None):
599 """DEPRECATED since IPython 5.0
599 """DEPRECATED since IPython 5.0
600
600
601 Enable event loop integration with pyglet.
601 Enable event loop integration with pyglet.
602
602
603 Parameters
603 Parameters
604 ----------
604 ----------
605 app : ignored
605 app : ignored
606 Ignored, it's only a placeholder to keep the call signature of all
606 Ignored, it's only a placeholder to keep the call signature of all
607 gui activation methods consistent, which simplifies the logic of
607 gui activation methods consistent, which simplifies the logic of
608 supporting magics.
608 supporting magics.
609
609
610 Notes
610 Notes
611 -----
611 -----
612 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
612 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
613 pyglet to integrate with terminal based applications like
613 pyglet to integrate with terminal based applications like
614 IPython.
614 IPython.
615
615
616 """
616 """
617 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
617 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
618 DeprecationWarning, stacklevel=2)
618 DeprecationWarning, stacklevel=2)
619 from IPython.lib.inputhookpyglet import inputhook_pyglet
619 from IPython.lib.inputhookpyglet import inputhook_pyglet
620 self.manager.set_inputhook(inputhook_pyglet)
620 self.manager.set_inputhook(inputhook_pyglet)
621 return app
621 return app
622
622
623
623
624 @inputhook_manager.register('gtk3')
624 @inputhook_manager.register('gtk3')
625 class Gtk3InputHook(InputHookBase):
625 class Gtk3InputHook(InputHookBase):
626 def enable(self, app=None):
626 def enable(self, app=None):
627 """DEPRECATED since IPython 5.0
627 """DEPRECATED since IPython 5.0
628
628
629 Enable event loop integration with Gtk3 (gir bindings).
629 Enable event loop integration with Gtk3 (gir bindings).
630
630
631 Parameters
631 Parameters
632 ----------
632 ----------
633 app : ignored
633 app : ignored
634 Ignored, it's only a placeholder to keep the call signature of all
634 Ignored, it's only a placeholder to keep the call signature of all
635 gui activation methods consistent, which simplifies the logic of
635 gui activation methods consistent, which simplifies the logic of
636 supporting magics.
636 supporting magics.
637
637
638 Notes
638 Notes
639 -----
639 -----
640 This methods sets the PyOS_InputHook for Gtk3, which allows
640 This methods sets the PyOS_InputHook for Gtk3, which allows
641 the Gtk3 to integrate with terminal based applications like
641 the Gtk3 to integrate with terminal based applications like
642 IPython.
642 IPython.
643 """
643 """
644 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
644 warn("This function is deprecated since IPython 5.0 and will be removed in future versions.",
645 DeprecationWarning, stacklevel=2)
645 DeprecationWarning, stacklevel=2)
646 from IPython.lib.inputhookgtk3 import inputhook_gtk3
646 from IPython.lib.inputhookgtk3 import inputhook_gtk3
647 self.manager.set_inputhook(inputhook_gtk3)
647 self.manager.set_inputhook(inputhook_gtk3)
648
648
649
649
650 clear_inputhook = inputhook_manager.clear_inputhook
650 clear_inputhook = inputhook_manager.clear_inputhook
651 set_inputhook = inputhook_manager.set_inputhook
651 set_inputhook = inputhook_manager.set_inputhook
652 current_gui = inputhook_manager.current_gui
652 current_gui = inputhook_manager.current_gui
653 clear_app_refs = inputhook_manager.clear_app_refs
653 clear_app_refs = inputhook_manager.clear_app_refs
654 enable_gui = inputhook_manager.enable_gui
654 enable_gui = inputhook_manager.enable_gui
655 disable_gui = inputhook_manager.disable_gui
655 disable_gui = inputhook_manager.disable_gui
656 register = inputhook_manager.register
656 register = inputhook_manager.register
657 guis = inputhook_manager.guihooks
657 guis = inputhook_manager.guihooks
658
658
659
659
660 def _deprecated_disable():
660 def _deprecated_disable():
661 warn("This function is deprecated since IPython 4.0 use disable_gui() instead", DeprecationWarning)
661 warn("This function is deprecated since IPython 4.0 use disable_gui() instead",
662 DeprecationWarning, stacklevel=2)
662 inputhook_manager.disable_gui()
663 inputhook_manager.disable_gui()
664
663 disable_wx = disable_qt4 = disable_gtk = disable_gtk3 = disable_glut = \
665 disable_wx = disable_qt4 = disable_gtk = disable_gtk3 = disable_glut = \
664 disable_pyglet = disable_osx = _deprecated_disable
666 disable_pyglet = disable_osx = _deprecated_disable
@@ -1,436 +1,439 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Suite Runner.
2 """IPython Test Suite Runner.
3
3
4 This module provides a main entry point to a user script to test IPython
4 This module provides a main entry point to a user script to test IPython
5 itself from the command line. There are two ways of running this script:
5 itself from the command line. There are two ways of running this script:
6
6
7 1. With the syntax `iptest all`. This runs our entire test suite by
7 1. With the syntax `iptest all`. This runs our entire test suite by
8 calling this script (with different arguments) recursively. This
8 calling this script (with different arguments) recursively. This
9 causes modules and package to be tested in different processes, using nose
9 causes modules and package to be tested in different processes, using nose
10 or trial where appropriate.
10 or trial where appropriate.
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 the script simply calls nose, but with special command line flags and
12 the script simply calls nose, but with special command line flags and
13 plugins loaded.
13 plugins loaded.
14
14
15 """
15 """
16
16
17 # Copyright (c) IPython Development Team.
17 # Copyright (c) IPython Development Team.
18 # Distributed under the terms of the Modified BSD License.
18 # Distributed under the terms of the Modified BSD License.
19
19
20
20
21 import glob
21 import glob
22 from io import BytesIO
22 from io import BytesIO
23 import os
23 import os
24 import os.path as path
24 import os.path as path
25 import sys
25 import sys
26 from threading import Thread, Lock, Event
26 from threading import Thread, Lock, Event
27 import warnings
27 import warnings
28
28
29 import nose.plugins.builtin
29 import nose.plugins.builtin
30 from nose.plugins.xunit import Xunit
30 from nose.plugins.xunit import Xunit
31 from nose import SkipTest
31 from nose import SkipTest
32 from nose.core import TestProgram
32 from nose.core import TestProgram
33 from nose.plugins import Plugin
33 from nose.plugins import Plugin
34 from nose.util import safe_str
34 from nose.util import safe_str
35
35
36 from IPython import version_info
36 from IPython import version_info
37 from IPython.utils.py3compat import bytes_to_str
37 from IPython.utils.py3compat import bytes_to_str
38 from IPython.utils.importstring import import_item
38 from IPython.utils.importstring import import_item
39 from IPython.testing.plugin.ipdoctest import IPythonDoctest
39 from IPython.testing.plugin.ipdoctest import IPythonDoctest
40 from IPython.external.decorators import KnownFailure, knownfailureif
40 from IPython.external.decorators import KnownFailure, knownfailureif
41
41
42 pjoin = path.join
42 pjoin = path.join
43
43
44
44
45 # Enable printing all warnings raise by IPython's modules
45 # Enable printing all warnings raise by IPython's modules
46 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
46 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
47 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
47 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
48 warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
48 warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
49 warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
49 warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
50
50
51 warnings.filterwarnings('error', message='.*apply_wrapper.*', category=DeprecationWarning, module='.*')
51 warnings.filterwarnings('error', message='.*apply_wrapper.*', category=DeprecationWarning, module='.*')
52 warnings.filterwarnings('error', message='.*make_label_dec', category=DeprecationWarning, module='.*')
52 warnings.filterwarnings('error', message='.*make_label_dec', category=DeprecationWarning, module='.*')
53 warnings.filterwarnings('error', message='.*decorated_dummy.*', category=DeprecationWarning, module='.*')
53 warnings.filterwarnings('error', message='.*decorated_dummy.*', category=DeprecationWarning, module='.*')
54 warnings.filterwarnings('error', message='.*skip_file_no_x11.*', category=DeprecationWarning, module='.*')
54 warnings.filterwarnings('error', message='.*skip_file_no_x11.*', category=DeprecationWarning, module='.*')
55 warnings.filterwarnings('error', message='.*onlyif_any_cmd_exists.*', category=DeprecationWarning, module='.*')
55 warnings.filterwarnings('error', message='.*onlyif_any_cmd_exists.*', category=DeprecationWarning, module='.*')
56
57 warnings.filterwarnings('error', message='.*disable_gui.*', category=DeprecationWarning, module='.*')
58
56 if version_info < (6,):
59 if version_info < (6,):
57 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
60 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
58 # warning with the runner they also import from standard import library. (as of Dec 2015)
61 # warning with the runner they also import from standard import library. (as of Dec 2015)
59 # Ignore, let's revisit that in a couple of years for IPython 6.
62 # Ignore, let's revisit that in a couple of years for IPython 6.
60 warnings.filterwarnings('ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
63 warnings.filterwarnings('ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
61
64
62
65
63 # ------------------------------------------------------------------------------
66 # ------------------------------------------------------------------------------
64 # Monkeypatch Xunit to count known failures as skipped.
67 # Monkeypatch Xunit to count known failures as skipped.
65 # ------------------------------------------------------------------------------
68 # ------------------------------------------------------------------------------
66 def monkeypatch_xunit():
69 def monkeypatch_xunit():
67 try:
70 try:
68 knownfailureif(True)(lambda: None)()
71 knownfailureif(True)(lambda: None)()
69 except Exception as e:
72 except Exception as e:
70 KnownFailureTest = type(e)
73 KnownFailureTest = type(e)
71
74
72 def addError(self, test, err, capt=None):
75 def addError(self, test, err, capt=None):
73 if issubclass(err[0], KnownFailureTest):
76 if issubclass(err[0], KnownFailureTest):
74 err = (SkipTest,) + err[1:]
77 err = (SkipTest,) + err[1:]
75 return self.orig_addError(test, err, capt)
78 return self.orig_addError(test, err, capt)
76
79
77 Xunit.orig_addError = Xunit.addError
80 Xunit.orig_addError = Xunit.addError
78 Xunit.addError = addError
81 Xunit.addError = addError
79
82
80 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
81 # Check which dependencies are installed and greater than minimum version.
84 # Check which dependencies are installed and greater than minimum version.
82 #-----------------------------------------------------------------------------
85 #-----------------------------------------------------------------------------
83 def extract_version(mod):
86 def extract_version(mod):
84 return mod.__version__
87 return mod.__version__
85
88
86 def test_for(item, min_version=None, callback=extract_version):
89 def test_for(item, min_version=None, callback=extract_version):
87 """Test to see if item is importable, and optionally check against a minimum
90 """Test to see if item is importable, and optionally check against a minimum
88 version.
91 version.
89
92
90 If min_version is given, the default behavior is to check against the
93 If min_version is given, the default behavior is to check against the
91 `__version__` attribute of the item, but specifying `callback` allows you to
94 `__version__` attribute of the item, but specifying `callback` allows you to
92 extract the value you are interested in. e.g::
95 extract the value you are interested in. e.g::
93
96
94 In [1]: import sys
97 In [1]: import sys
95
98
96 In [2]: from IPython.testing.iptest import test_for
99 In [2]: from IPython.testing.iptest import test_for
97
100
98 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
101 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
99 Out[3]: True
102 Out[3]: True
100
103
101 """
104 """
102 try:
105 try:
103 check = import_item(item)
106 check = import_item(item)
104 except (ImportError, RuntimeError):
107 except (ImportError, RuntimeError):
105 # GTK reports Runtime error if it can't be initialized even if it's
108 # GTK reports Runtime error if it can't be initialized even if it's
106 # importable.
109 # importable.
107 return False
110 return False
108 else:
111 else:
109 if min_version:
112 if min_version:
110 if callback:
113 if callback:
111 # extra processing step to get version to compare
114 # extra processing step to get version to compare
112 check = callback(check)
115 check = callback(check)
113
116
114 return check >= min_version
117 return check >= min_version
115 else:
118 else:
116 return True
119 return True
117
120
118 # Global dict where we can store information on what we have and what we don't
121 # Global dict where we can store information on what we have and what we don't
119 # have available at test run time
122 # have available at test run time
120 have = {'matplotlib': test_for('matplotlib'),
123 have = {'matplotlib': test_for('matplotlib'),
121 'pygments': test_for('pygments'),
124 'pygments': test_for('pygments'),
122 'sqlite3': test_for('sqlite3')}
125 'sqlite3': test_for('sqlite3')}
123
126
124 #-----------------------------------------------------------------------------
127 #-----------------------------------------------------------------------------
125 # Test suite definitions
128 # Test suite definitions
126 #-----------------------------------------------------------------------------
129 #-----------------------------------------------------------------------------
127
130
128 test_group_names = ['core',
131 test_group_names = ['core',
129 'extensions', 'lib', 'terminal', 'testing', 'utils',
132 'extensions', 'lib', 'terminal', 'testing', 'utils',
130 ]
133 ]
131
134
132 class TestSection(object):
135 class TestSection(object):
133 def __init__(self, name, includes):
136 def __init__(self, name, includes):
134 self.name = name
137 self.name = name
135 self.includes = includes
138 self.includes = includes
136 self.excludes = []
139 self.excludes = []
137 self.dependencies = []
140 self.dependencies = []
138 self.enabled = True
141 self.enabled = True
139
142
140 def exclude(self, module):
143 def exclude(self, module):
141 if not module.startswith('IPython'):
144 if not module.startswith('IPython'):
142 module = self.includes[0] + "." + module
145 module = self.includes[0] + "." + module
143 self.excludes.append(module.replace('.', os.sep))
146 self.excludes.append(module.replace('.', os.sep))
144
147
145 def requires(self, *packages):
148 def requires(self, *packages):
146 self.dependencies.extend(packages)
149 self.dependencies.extend(packages)
147
150
148 @property
151 @property
149 def will_run(self):
152 def will_run(self):
150 return self.enabled and all(have[p] for p in self.dependencies)
153 return self.enabled and all(have[p] for p in self.dependencies)
151
154
152 # Name -> (include, exclude, dependencies_met)
155 # Name -> (include, exclude, dependencies_met)
153 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
156 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
154
157
155
158
156 # Exclusions and dependencies
159 # Exclusions and dependencies
157 # ---------------------------
160 # ---------------------------
158
161
159 # core:
162 # core:
160 sec = test_sections['core']
163 sec = test_sections['core']
161 if not have['sqlite3']:
164 if not have['sqlite3']:
162 sec.exclude('tests.test_history')
165 sec.exclude('tests.test_history')
163 sec.exclude('history')
166 sec.exclude('history')
164 if not have['matplotlib']:
167 if not have['matplotlib']:
165 sec.exclude('pylabtools'),
168 sec.exclude('pylabtools'),
166 sec.exclude('tests.test_pylabtools')
169 sec.exclude('tests.test_pylabtools')
167
170
168 # lib:
171 # lib:
169 sec = test_sections['lib']
172 sec = test_sections['lib']
170 sec.exclude('kernel')
173 sec.exclude('kernel')
171 if not have['pygments']:
174 if not have['pygments']:
172 sec.exclude('tests.test_lexers')
175 sec.exclude('tests.test_lexers')
173 # We do this unconditionally, so that the test suite doesn't import
176 # We do this unconditionally, so that the test suite doesn't import
174 # gtk, changing the default encoding and masking some unicode bugs.
177 # gtk, changing the default encoding and masking some unicode bugs.
175 sec.exclude('inputhookgtk')
178 sec.exclude('inputhookgtk')
176 # We also do this unconditionally, because wx can interfere with Unix signals.
179 # We also do this unconditionally, because wx can interfere with Unix signals.
177 # There are currently no tests for it anyway.
180 # There are currently no tests for it anyway.
178 sec.exclude('inputhookwx')
181 sec.exclude('inputhookwx')
179 # Testing inputhook will need a lot of thought, to figure out
182 # Testing inputhook will need a lot of thought, to figure out
180 # how to have tests that don't lock up with the gui event
183 # how to have tests that don't lock up with the gui event
181 # loops in the picture
184 # loops in the picture
182 sec.exclude('inputhook')
185 sec.exclude('inputhook')
183
186
184 # testing:
187 # testing:
185 sec = test_sections['testing']
188 sec = test_sections['testing']
186 # These have to be skipped on win32 because they use echo, rm, cd, etc.
189 # These have to be skipped on win32 because they use echo, rm, cd, etc.
187 # See ticket https://github.com/ipython/ipython/issues/87
190 # See ticket https://github.com/ipython/ipython/issues/87
188 if sys.platform == 'win32':
191 if sys.platform == 'win32':
189 sec.exclude('plugin.test_exampleip')
192 sec.exclude('plugin.test_exampleip')
190 sec.exclude('plugin.dtexample')
193 sec.exclude('plugin.dtexample')
191
194
192 # don't run jupyter_console tests found via shim
195 # don't run jupyter_console tests found via shim
193 test_sections['terminal'].exclude('console')
196 test_sections['terminal'].exclude('console')
194
197
195 # extensions:
198 # extensions:
196 sec = test_sections['extensions']
199 sec = test_sections['extensions']
197 # This is deprecated in favour of rpy2
200 # This is deprecated in favour of rpy2
198 sec.exclude('rmagic')
201 sec.exclude('rmagic')
199 # autoreload does some strange stuff, so move it to its own test section
202 # autoreload does some strange stuff, so move it to its own test section
200 sec.exclude('autoreload')
203 sec.exclude('autoreload')
201 sec.exclude('tests.test_autoreload')
204 sec.exclude('tests.test_autoreload')
202 test_sections['autoreload'] = TestSection('autoreload',
205 test_sections['autoreload'] = TestSection('autoreload',
203 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
206 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
204 test_group_names.append('autoreload')
207 test_group_names.append('autoreload')
205
208
206
209
207 #-----------------------------------------------------------------------------
210 #-----------------------------------------------------------------------------
208 # Functions and classes
211 # Functions and classes
209 #-----------------------------------------------------------------------------
212 #-----------------------------------------------------------------------------
210
213
211 def check_exclusions_exist():
214 def check_exclusions_exist():
212 from IPython.paths import get_ipython_package_dir
215 from IPython.paths import get_ipython_package_dir
213 from warnings import warn
216 from warnings import warn
214 parent = os.path.dirname(get_ipython_package_dir())
217 parent = os.path.dirname(get_ipython_package_dir())
215 for sec in test_sections:
218 for sec in test_sections:
216 for pattern in sec.exclusions:
219 for pattern in sec.exclusions:
217 fullpath = pjoin(parent, pattern)
220 fullpath = pjoin(parent, pattern)
218 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
221 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
219 warn("Excluding nonexistent file: %r" % pattern)
222 warn("Excluding nonexistent file: %r" % pattern)
220
223
221
224
222 class ExclusionPlugin(Plugin):
225 class ExclusionPlugin(Plugin):
223 """A nose plugin to effect our exclusions of files and directories.
226 """A nose plugin to effect our exclusions of files and directories.
224 """
227 """
225 name = 'exclusions'
228 name = 'exclusions'
226 score = 3000 # Should come before any other plugins
229 score = 3000 # Should come before any other plugins
227
230
228 def __init__(self, exclude_patterns=None):
231 def __init__(self, exclude_patterns=None):
229 """
232 """
230 Parameters
233 Parameters
231 ----------
234 ----------
232
235
233 exclude_patterns : sequence of strings, optional
236 exclude_patterns : sequence of strings, optional
234 Filenames containing these patterns (as raw strings, not as regular
237 Filenames containing these patterns (as raw strings, not as regular
235 expressions) are excluded from the tests.
238 expressions) are excluded from the tests.
236 """
239 """
237 self.exclude_patterns = exclude_patterns or []
240 self.exclude_patterns = exclude_patterns or []
238 super(ExclusionPlugin, self).__init__()
241 super(ExclusionPlugin, self).__init__()
239
242
240 def options(self, parser, env=os.environ):
243 def options(self, parser, env=os.environ):
241 Plugin.options(self, parser, env)
244 Plugin.options(self, parser, env)
242
245
243 def configure(self, options, config):
246 def configure(self, options, config):
244 Plugin.configure(self, options, config)
247 Plugin.configure(self, options, config)
245 # Override nose trying to disable plugin.
248 # Override nose trying to disable plugin.
246 self.enabled = True
249 self.enabled = True
247
250
248 def wantFile(self, filename):
251 def wantFile(self, filename):
249 """Return whether the given filename should be scanned for tests.
252 """Return whether the given filename should be scanned for tests.
250 """
253 """
251 if any(pat in filename for pat in self.exclude_patterns):
254 if any(pat in filename for pat in self.exclude_patterns):
252 return False
255 return False
253 return None
256 return None
254
257
255 def wantDirectory(self, directory):
258 def wantDirectory(self, directory):
256 """Return whether the given directory should be scanned for tests.
259 """Return whether the given directory should be scanned for tests.
257 """
260 """
258 if any(pat in directory for pat in self.exclude_patterns):
261 if any(pat in directory for pat in self.exclude_patterns):
259 return False
262 return False
260 return None
263 return None
261
264
262
265
263 class StreamCapturer(Thread):
266 class StreamCapturer(Thread):
264 daemon = True # Don't hang if main thread crashes
267 daemon = True # Don't hang if main thread crashes
265 started = False
268 started = False
266 def __init__(self, echo=False):
269 def __init__(self, echo=False):
267 super(StreamCapturer, self).__init__()
270 super(StreamCapturer, self).__init__()
268 self.echo = echo
271 self.echo = echo
269 self.streams = []
272 self.streams = []
270 self.buffer = BytesIO()
273 self.buffer = BytesIO()
271 self.readfd, self.writefd = os.pipe()
274 self.readfd, self.writefd = os.pipe()
272 self.buffer_lock = Lock()
275 self.buffer_lock = Lock()
273 self.stop = Event()
276 self.stop = Event()
274
277
275 def run(self):
278 def run(self):
276 self.started = True
279 self.started = True
277
280
278 while not self.stop.is_set():
281 while not self.stop.is_set():
279 chunk = os.read(self.readfd, 1024)
282 chunk = os.read(self.readfd, 1024)
280
283
281 with self.buffer_lock:
284 with self.buffer_lock:
282 self.buffer.write(chunk)
285 self.buffer.write(chunk)
283 if self.echo:
286 if self.echo:
284 sys.stdout.write(bytes_to_str(chunk))
287 sys.stdout.write(bytes_to_str(chunk))
285
288
286 os.close(self.readfd)
289 os.close(self.readfd)
287 os.close(self.writefd)
290 os.close(self.writefd)
288
291
289 def reset_buffer(self):
292 def reset_buffer(self):
290 with self.buffer_lock:
293 with self.buffer_lock:
291 self.buffer.truncate(0)
294 self.buffer.truncate(0)
292 self.buffer.seek(0)
295 self.buffer.seek(0)
293
296
294 def get_buffer(self):
297 def get_buffer(self):
295 with self.buffer_lock:
298 with self.buffer_lock:
296 return self.buffer.getvalue()
299 return self.buffer.getvalue()
297
300
298 def ensure_started(self):
301 def ensure_started(self):
299 if not self.started:
302 if not self.started:
300 self.start()
303 self.start()
301
304
302 def halt(self):
305 def halt(self):
303 """Safely stop the thread."""
306 """Safely stop the thread."""
304 if not self.started:
307 if not self.started:
305 return
308 return
306
309
307 self.stop.set()
310 self.stop.set()
308 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
311 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
309 self.join()
312 self.join()
310
313
311 class SubprocessStreamCapturePlugin(Plugin):
314 class SubprocessStreamCapturePlugin(Plugin):
312 name='subprocstreams'
315 name='subprocstreams'
313 def __init__(self):
316 def __init__(self):
314 Plugin.__init__(self)
317 Plugin.__init__(self)
315 self.stream_capturer = StreamCapturer()
318 self.stream_capturer = StreamCapturer()
316 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
319 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
317 # This is ugly, but distant parts of the test machinery need to be able
320 # This is ugly, but distant parts of the test machinery need to be able
318 # to redirect streams, so we make the object globally accessible.
321 # to redirect streams, so we make the object globally accessible.
319 nose.iptest_stdstreams_fileno = self.get_write_fileno
322 nose.iptest_stdstreams_fileno = self.get_write_fileno
320
323
321 def get_write_fileno(self):
324 def get_write_fileno(self):
322 if self.destination == 'capture':
325 if self.destination == 'capture':
323 self.stream_capturer.ensure_started()
326 self.stream_capturer.ensure_started()
324 return self.stream_capturer.writefd
327 return self.stream_capturer.writefd
325 elif self.destination == 'discard':
328 elif self.destination == 'discard':
326 return os.open(os.devnull, os.O_WRONLY)
329 return os.open(os.devnull, os.O_WRONLY)
327 else:
330 else:
328 return sys.__stdout__.fileno()
331 return sys.__stdout__.fileno()
329
332
330 def configure(self, options, config):
333 def configure(self, options, config):
331 Plugin.configure(self, options, config)
334 Plugin.configure(self, options, config)
332 # Override nose trying to disable plugin.
335 # Override nose trying to disable plugin.
333 if self.destination == 'capture':
336 if self.destination == 'capture':
334 self.enabled = True
337 self.enabled = True
335
338
336 def startTest(self, test):
339 def startTest(self, test):
337 # Reset log capture
340 # Reset log capture
338 self.stream_capturer.reset_buffer()
341 self.stream_capturer.reset_buffer()
339
342
340 def formatFailure(self, test, err):
343 def formatFailure(self, test, err):
341 # Show output
344 # Show output
342 ec, ev, tb = err
345 ec, ev, tb = err
343 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
346 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
344 if captured.strip():
347 if captured.strip():
345 ev = safe_str(ev)
348 ev = safe_str(ev)
346 out = [ev, '>> begin captured subprocess output <<',
349 out = [ev, '>> begin captured subprocess output <<',
347 captured,
350 captured,
348 '>> end captured subprocess output <<']
351 '>> end captured subprocess output <<']
349 return ec, '\n'.join(out), tb
352 return ec, '\n'.join(out), tb
350
353
351 return err
354 return err
352
355
353 formatError = formatFailure
356 formatError = formatFailure
354
357
355 def finalize(self, result):
358 def finalize(self, result):
356 self.stream_capturer.halt()
359 self.stream_capturer.halt()
357
360
358
361
359 def run_iptest():
362 def run_iptest():
360 """Run the IPython test suite using nose.
363 """Run the IPython test suite using nose.
361
364
362 This function is called when this script is **not** called with the form
365 This function is called when this script is **not** called with the form
363 `iptest all`. It simply calls nose with appropriate command line flags
366 `iptest all`. It simply calls nose with appropriate command line flags
364 and accepts all of the standard nose arguments.
367 and accepts all of the standard nose arguments.
365 """
368 """
366 # Apply our monkeypatch to Xunit
369 # Apply our monkeypatch to Xunit
367 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
370 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
368 monkeypatch_xunit()
371 monkeypatch_xunit()
369
372
370 arg1 = sys.argv[1]
373 arg1 = sys.argv[1]
371 if arg1 in test_sections:
374 if arg1 in test_sections:
372 section = test_sections[arg1]
375 section = test_sections[arg1]
373 sys.argv[1:2] = section.includes
376 sys.argv[1:2] = section.includes
374 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
377 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
375 section = test_sections[arg1[8:]]
378 section = test_sections[arg1[8:]]
376 sys.argv[1:2] = section.includes
379 sys.argv[1:2] = section.includes
377 else:
380 else:
378 section = TestSection(arg1, includes=[arg1])
381 section = TestSection(arg1, includes=[arg1])
379
382
380
383
381 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
384 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
382 # We add --exe because of setuptools' imbecility (it
385 # We add --exe because of setuptools' imbecility (it
383 # blindly does chmod +x on ALL files). Nose does the
386 # blindly does chmod +x on ALL files). Nose does the
384 # right thing and it tries to avoid executables,
387 # right thing and it tries to avoid executables,
385 # setuptools unfortunately forces our hand here. This
388 # setuptools unfortunately forces our hand here. This
386 # has been discussed on the distutils list and the
389 # has been discussed on the distutils list and the
387 # setuptools devs refuse to fix this problem!
390 # setuptools devs refuse to fix this problem!
388 '--exe',
391 '--exe',
389 ]
392 ]
390 if '-a' not in argv and '-A' not in argv:
393 if '-a' not in argv and '-A' not in argv:
391 argv = argv + ['-a', '!crash']
394 argv = argv + ['-a', '!crash']
392
395
393 if nose.__version__ >= '0.11':
396 if nose.__version__ >= '0.11':
394 # I don't fully understand why we need this one, but depending on what
397 # I don't fully understand why we need this one, but depending on what
395 # directory the test suite is run from, if we don't give it, 0 tests
398 # directory the test suite is run from, if we don't give it, 0 tests
396 # get run. Specifically, if the test suite is run from the source dir
399 # get run. Specifically, if the test suite is run from the source dir
397 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
400 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
398 # even if the same call done in this directory works fine). It appears
401 # even if the same call done in this directory works fine). It appears
399 # that if the requested package is in the current dir, nose bails early
402 # that if the requested package is in the current dir, nose bails early
400 # by default. Since it's otherwise harmless, leave it in by default
403 # by default. Since it's otherwise harmless, leave it in by default
401 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
404 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
402 argv.append('--traverse-namespace')
405 argv.append('--traverse-namespace')
403
406
404 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
407 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
405 SubprocessStreamCapturePlugin() ]
408 SubprocessStreamCapturePlugin() ]
406
409
407 # we still have some vestigial doctests in core
410 # we still have some vestigial doctests in core
408 if (section.name.startswith(('core', 'IPython.core', 'IPython.utils'))):
411 if (section.name.startswith(('core', 'IPython.core', 'IPython.utils'))):
409 plugins.append(IPythonDoctest())
412 plugins.append(IPythonDoctest())
410 argv.extend([
413 argv.extend([
411 '--with-ipdoctest',
414 '--with-ipdoctest',
412 '--ipdoctest-tests',
415 '--ipdoctest-tests',
413 '--ipdoctest-extension=txt',
416 '--ipdoctest-extension=txt',
414 ])
417 ])
415
418
416
419
417 # Use working directory set by parent process (see iptestcontroller)
420 # Use working directory set by parent process (see iptestcontroller)
418 if 'IPTEST_WORKING_DIR' in os.environ:
421 if 'IPTEST_WORKING_DIR' in os.environ:
419 os.chdir(os.environ['IPTEST_WORKING_DIR'])
422 os.chdir(os.environ['IPTEST_WORKING_DIR'])
420
423
421 # We need a global ipython running in this process, but the special
424 # We need a global ipython running in this process, but the special
422 # in-process group spawns its own IPython kernels, so for *that* group we
425 # in-process group spawns its own IPython kernels, so for *that* group we
423 # must avoid also opening the global one (otherwise there's a conflict of
426 # must avoid also opening the global one (otherwise there's a conflict of
424 # singletons). Ultimately the solution to this problem is to refactor our
427 # singletons). Ultimately the solution to this problem is to refactor our
425 # assumptions about what needs to be a singleton and what doesn't (app
428 # assumptions about what needs to be a singleton and what doesn't (app
426 # objects should, individual shells shouldn't). But for now, this
429 # objects should, individual shells shouldn't). But for now, this
427 # workaround allows the test suite for the inprocess module to complete.
430 # workaround allows the test suite for the inprocess module to complete.
428 if 'kernel.inprocess' not in section.name:
431 if 'kernel.inprocess' not in section.name:
429 from IPython.testing import globalipapp
432 from IPython.testing import globalipapp
430 globalipapp.start_ipython()
433 globalipapp.start_ipython()
431
434
432 # Now nose can run
435 # Now nose can run
433 TestProgram(argv=argv, addplugins=plugins)
436 TestProgram(argv=argv, addplugins=plugins)
434
437
435 if __name__ == '__main__':
438 if __name__ == '__main__':
436 run_iptest()
439 run_iptest()
General Comments 0
You need to be logged in to leave comments. Login now