##// END OF EJS Templates
Backport PR #10039: Improve deprecation warnings...
Thomas Kluyver -
Show More
@@ -1,630 +1,630 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Pdb debugger class.
3 Pdb debugger class.
4
4
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 the command line completion of other programs which include this isn't
6 the command line completion of other programs which include this isn't
7 damaged.
7 damaged.
8
8
9 In the future, this class will be expanded with improvements over the standard
9 In the future, this class will be expanded with improvements over the standard
10 pdb.
10 pdb.
11
11
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 changes. Licensing should therefore be under the standard Python terms. For
13 changes. Licensing should therefore be under the standard Python terms. For
14 details on the PSF (Python Software Foundation) standard license, see:
14 details on the PSF (Python Software Foundation) standard license, see:
15
15
16 http://www.python.org/2.2.3/license.html"""
16 http://www.python.org/2.2.3/license.html"""
17
17
18 #*****************************************************************************
18 #*****************************************************************************
19 #
19 #
20 # This file is licensed under the PSF license.
20 # This file is licensed under the PSF license.
21 #
21 #
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
22 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 #
24 #
25 #
25 #
26 #*****************************************************************************
26 #*****************************************************************************
27 from __future__ import print_function
27 from __future__ import print_function
28
28
29 import bdb
29 import bdb
30 import functools
30 import functools
31 import inspect
31 import inspect
32 import sys
32 import sys
33 import warnings
33 import warnings
34
34
35 from IPython import get_ipython
35 from IPython import get_ipython
36 from IPython.utils import PyColorize, ulinecache
36 from IPython.utils import PyColorize, ulinecache
37 from IPython.utils import coloransi, py3compat
37 from IPython.utils import coloransi, py3compat
38 from IPython.core.excolors import exception_colors
38 from IPython.core.excolors import exception_colors
39 from IPython.testing.skipdoctest import skip_doctest
39 from IPython.testing.skipdoctest import skip_doctest
40
40
41
41
42 prompt = 'ipdb> '
42 prompt = 'ipdb> '
43
43
44 #We have to check this directly from sys.argv, config struct not yet available
44 #We have to check this directly from sys.argv, config struct not yet available
45 from pdb import Pdb as OldPdb
45 from pdb import Pdb as OldPdb
46
46
47 # Allow the set_trace code to operate outside of an ipython instance, even if
47 # Allow the set_trace code to operate outside of an ipython instance, even if
48 # it does so with some limitations. The rest of this support is implemented in
48 # it does so with some limitations. The rest of this support is implemented in
49 # the Tracer constructor.
49 # the Tracer constructor.
50
50
51 def make_arrow(pad):
51 def make_arrow(pad):
52 """generate the leading arrow in front of traceback or debugger"""
52 """generate the leading arrow in front of traceback or debugger"""
53 if pad >= 2:
53 if pad >= 2:
54 return '-'*(pad-2) + '> '
54 return '-'*(pad-2) + '> '
55 elif pad == 1:
55 elif pad == 1:
56 return '>'
56 return '>'
57 return ''
57 return ''
58
58
59
59
60 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
60 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
61 """Exception hook which handles `BdbQuit` exceptions.
61 """Exception hook which handles `BdbQuit` exceptions.
62
62
63 All other exceptions are processed using the `excepthook`
63 All other exceptions are processed using the `excepthook`
64 parameter.
64 parameter.
65 """
65 """
66 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
66 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
67 DeprecationWarning)
67 DeprecationWarning, stacklevel=2)
68 if et==bdb.BdbQuit:
68 if et==bdb.BdbQuit:
69 print('Exiting Debugger.')
69 print('Exiting Debugger.')
70 elif excepthook is not None:
70 elif excepthook is not None:
71 excepthook(et, ev, tb)
71 excepthook(et, ev, tb)
72 else:
72 else:
73 # Backwards compatibility. Raise deprecation warning?
73 # Backwards compatibility. Raise deprecation warning?
74 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
74 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
75
75
76
76
77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
78 warnings.warn(
78 warnings.warn(
79 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
79 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
80 DeprecationWarning)
80 DeprecationWarning, stacklevel=2)
81 print('Exiting Debugger.')
81 print('Exiting Debugger.')
82
82
83
83
84 class Tracer(object):
84 class Tracer(object):
85 """
85 """
86 DEPRECATED
86 DEPRECATED
87
87
88 Class for local debugging, similar to pdb.set_trace.
88 Class for local debugging, similar to pdb.set_trace.
89
89
90 Instances of this class, when called, behave like pdb.set_trace, but
90 Instances of this class, when called, behave like pdb.set_trace, but
91 providing IPython's enhanced capabilities.
91 providing IPython's enhanced capabilities.
92
92
93 This is implemented as a class which must be initialized in your own code
93 This is implemented as a class which must be initialized in your own code
94 and not as a standalone function because we need to detect at runtime
94 and not as a standalone function because we need to detect at runtime
95 whether IPython is already active or not. That detection is done in the
95 whether IPython is already active or not. That detection is done in the
96 constructor, ensuring that this code plays nicely with a running IPython,
96 constructor, ensuring that this code plays nicely with a running IPython,
97 while functioning acceptably (though with limitations) if outside of it.
97 while functioning acceptably (though with limitations) if outside of it.
98 """
98 """
99
99
100 @skip_doctest
100 @skip_doctest
101 def __init__(self, colors=None):
101 def __init__(self, colors=None):
102 """
102 """
103 DEPRECATED
103 DEPRECATED
104
104
105 Create a local debugger instance.
105 Create a local debugger instance.
106
106
107 Parameters
107 Parameters
108 ----------
108 ----------
109
109
110 colors : str, optional
110 colors : str, optional
111 The name of the color scheme to use, it must be one of IPython's
111 The name of the color scheme to use, it must be one of IPython's
112 valid color schemes. If not given, the function will default to
112 valid color schemes. If not given, the function will default to
113 the current IPython scheme when running inside IPython, and to
113 the current IPython scheme when running inside IPython, and to
114 'NoColor' otherwise.
114 'NoColor' otherwise.
115
115
116 Examples
116 Examples
117 --------
117 --------
118 ::
118 ::
119
119
120 from IPython.core.debugger import Tracer; debug_here = Tracer()
120 from IPython.core.debugger import Tracer; debug_here = Tracer()
121
121
122 Later in your code::
122 Later in your code::
123
123
124 debug_here() # -> will open up the debugger at that point.
124 debug_here() # -> will open up the debugger at that point.
125
125
126 Once the debugger activates, you can use all of its regular commands to
126 Once the debugger activates, you can use all of its regular commands to
127 step through code, set breakpoints, etc. See the pdb documentation
127 step through code, set breakpoints, etc. See the pdb documentation
128 from the Python standard library for usage details.
128 from the Python standard library for usage details.
129 """
129 """
130 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
130 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
131 "`IPython.core.debugger.Pdb.set_trace()`",
131 "`IPython.core.debugger.Pdb.set_trace()`",
132 DeprecationWarning)
132 DeprecationWarning, stacklevel=2)
133
133
134 ip = get_ipython()
134 ip = get_ipython()
135 if ip is None:
135 if ip is None:
136 # Outside of ipython, we set our own exception hook manually
136 # Outside of ipython, we set our own exception hook manually
137 sys.excepthook = functools.partial(BdbQuit_excepthook,
137 sys.excepthook = functools.partial(BdbQuit_excepthook,
138 excepthook=sys.excepthook)
138 excepthook=sys.excepthook)
139 def_colors = 'NoColor'
139 def_colors = 'NoColor'
140 else:
140 else:
141 # In ipython, we use its custom exception handler mechanism
141 # In ipython, we use its custom exception handler mechanism
142 def_colors = ip.colors
142 def_colors = ip.colors
143 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
143 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
144
144
145 if colors is None:
145 if colors is None:
146 colors = def_colors
146 colors = def_colors
147
147
148 # The stdlib debugger internally uses a modified repr from the `repr`
148 # The stdlib debugger internally uses a modified repr from the `repr`
149 # module, that limits the length of printed strings to a hardcoded
149 # module, that limits the length of printed strings to a hardcoded
150 # limit of 30 characters. That much trimming is too aggressive, let's
150 # limit of 30 characters. That much trimming is too aggressive, let's
151 # at least raise that limit to 80 chars, which should be enough for
151 # at least raise that limit to 80 chars, which should be enough for
152 # most interactive uses.
152 # most interactive uses.
153 try:
153 try:
154 try:
154 try:
155 from reprlib import aRepr # Py 3
155 from reprlib import aRepr # Py 3
156 except ImportError:
156 except ImportError:
157 from repr import aRepr # Py 2
157 from repr import aRepr # Py 2
158 aRepr.maxstring = 80
158 aRepr.maxstring = 80
159 except:
159 except:
160 # This is only a user-facing convenience, so any error we encounter
160 # This is only a user-facing convenience, so any error we encounter
161 # here can be warned about but can be otherwise ignored. These
161 # here can be warned about but can be otherwise ignored. These
162 # printouts will tell us about problems if this API changes
162 # printouts will tell us about problems if this API changes
163 import traceback
163 import traceback
164 traceback.print_exc()
164 traceback.print_exc()
165
165
166 self.debugger = Pdb(colors)
166 self.debugger = Pdb(colors)
167
167
168 def __call__(self):
168 def __call__(self):
169 """Starts an interactive debugger at the point where called.
169 """Starts an interactive debugger at the point where called.
170
170
171 This is similar to the pdb.set_trace() function from the std lib, but
171 This is similar to the pdb.set_trace() function from the std lib, but
172 using IPython's enhanced debugger."""
172 using IPython's enhanced debugger."""
173
173
174 self.debugger.set_trace(sys._getframe().f_back)
174 self.debugger.set_trace(sys._getframe().f_back)
175
175
176
176
177 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
177 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
178 """Make new_fn have old_fn's doc string. This is particularly useful
178 """Make new_fn have old_fn's doc string. This is particularly useful
179 for the ``do_...`` commands that hook into the help system.
179 for the ``do_...`` commands that hook into the help system.
180 Adapted from from a comp.lang.python posting
180 Adapted from from a comp.lang.python posting
181 by Duncan Booth."""
181 by Duncan Booth."""
182 def wrapper(*args, **kw):
182 def wrapper(*args, **kw):
183 return new_fn(*args, **kw)
183 return new_fn(*args, **kw)
184 if old_fn.__doc__:
184 if old_fn.__doc__:
185 wrapper.__doc__ = old_fn.__doc__ + additional_text
185 wrapper.__doc__ = old_fn.__doc__ + additional_text
186 return wrapper
186 return wrapper
187
187
188
188
189 def _file_lines(fname):
189 def _file_lines(fname):
190 """Return the contents of a named file as a list of lines.
190 """Return the contents of a named file as a list of lines.
191
191
192 This function never raises an IOError exception: if the file can't be
192 This function never raises an IOError exception: if the file can't be
193 read, it simply returns an empty list."""
193 read, it simply returns an empty list."""
194
194
195 try:
195 try:
196 outfile = open(fname)
196 outfile = open(fname)
197 except IOError:
197 except IOError:
198 return []
198 return []
199 else:
199 else:
200 out = outfile.readlines()
200 out = outfile.readlines()
201 outfile.close()
201 outfile.close()
202 return out
202 return out
203
203
204
204
205 class Pdb(OldPdb, object):
205 class Pdb(OldPdb, object):
206 """Modified Pdb class, does not load readline.
206 """Modified Pdb class, does not load readline.
207
207
208 for a standalone version that uses prompt_toolkit, see
208 for a standalone version that uses prompt_toolkit, see
209 `IPython.terminal.debugger.TerminalPdb` and
209 `IPython.terminal.debugger.TerminalPdb` and
210 `IPython.terminal.debugger.set_trace()`
210 `IPython.terminal.debugger.set_trace()`
211 """
211 """
212
212
213 def __init__(self, color_scheme=None, completekey=None,
213 def __init__(self, color_scheme=None, completekey=None,
214 stdin=None, stdout=None, context=5):
214 stdin=None, stdout=None, context=5):
215
215
216 # Parent constructor:
216 # Parent constructor:
217 try:
217 try:
218 self.context = int(context)
218 self.context = int(context)
219 if self.context <= 0:
219 if self.context <= 0:
220 raise ValueError("Context must be a positive integer")
220 raise ValueError("Context must be a positive integer")
221 except (TypeError, ValueError):
221 except (TypeError, ValueError):
222 raise ValueError("Context must be a positive integer")
222 raise ValueError("Context must be a positive integer")
223
223
224 OldPdb.__init__(self, completekey, stdin, stdout)
224 OldPdb.__init__(self, completekey, stdin, stdout)
225
225
226 # IPython changes...
226 # IPython changes...
227 self.shell = get_ipython()
227 self.shell = get_ipython()
228
228
229 if self.shell is None:
229 if self.shell is None:
230 save_main = sys.modules['__main__']
230 save_main = sys.modules['__main__']
231 # No IPython instance running, we must create one
231 # No IPython instance running, we must create one
232 from IPython.terminal.interactiveshell import \
232 from IPython.terminal.interactiveshell import \
233 TerminalInteractiveShell
233 TerminalInteractiveShell
234 self.shell = TerminalInteractiveShell.instance()
234 self.shell = TerminalInteractiveShell.instance()
235 # needed by any code which calls __import__("__main__") after
235 # needed by any code which calls __import__("__main__") after
236 # the debugger was entered. See also #9941.
236 # the debugger was entered. See also #9941.
237 sys.modules['__main__'] = save_main
237 sys.modules['__main__'] = save_main
238
238
239 if color_scheme is not None:
239 if color_scheme is not None:
240 warnings.warn(
240 warnings.warn(
241 "The `color_scheme` argument is deprecated since version 5.1",
241 "The `color_scheme` argument is deprecated since version 5.1",
242 DeprecationWarning)
242 DeprecationWarning)
243 else:
243 else:
244 color_scheme = self.shell.colors
244 color_scheme = self.shell.colors
245
245
246 self.aliases = {}
246 self.aliases = {}
247
247
248 # Create color table: we copy the default one from the traceback
248 # Create color table: we copy the default one from the traceback
249 # module and add a few attributes needed for debugging
249 # module and add a few attributes needed for debugging
250 self.color_scheme_table = exception_colors()
250 self.color_scheme_table = exception_colors()
251
251
252 # shorthands
252 # shorthands
253 C = coloransi.TermColors
253 C = coloransi.TermColors
254 cst = self.color_scheme_table
254 cst = self.color_scheme_table
255
255
256 cst['NoColor'].colors.prompt = C.NoColor
256 cst['NoColor'].colors.prompt = C.NoColor
257 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
257 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
258 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
258 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
259
259
260 cst['Linux'].colors.prompt = C.Green
260 cst['Linux'].colors.prompt = C.Green
261 cst['Linux'].colors.breakpoint_enabled = C.LightRed
261 cst['Linux'].colors.breakpoint_enabled = C.LightRed
262 cst['Linux'].colors.breakpoint_disabled = C.Red
262 cst['Linux'].colors.breakpoint_disabled = C.Red
263
263
264 cst['LightBG'].colors.prompt = C.Blue
264 cst['LightBG'].colors.prompt = C.Blue
265 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
265 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
266 cst['LightBG'].colors.breakpoint_disabled = C.Red
266 cst['LightBG'].colors.breakpoint_disabled = C.Red
267
267
268 cst['Neutral'].colors.prompt = C.Blue
268 cst['Neutral'].colors.prompt = C.Blue
269 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
269 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
270 cst['Neutral'].colors.breakpoint_disabled = C.Red
270 cst['Neutral'].colors.breakpoint_disabled = C.Red
271
271
272 self.set_colors(color_scheme)
272 self.set_colors(color_scheme)
273
273
274 # Add a python parser so we can syntax highlight source while
274 # Add a python parser so we can syntax highlight source while
275 # debugging.
275 # debugging.
276 self.parser = PyColorize.Parser()
276 self.parser = PyColorize.Parser()
277
277
278 # Set the prompt - the default prompt is '(Pdb)'
278 # Set the prompt - the default prompt is '(Pdb)'
279 self.prompt = prompt
279 self.prompt = prompt
280
280
281 def set_colors(self, scheme):
281 def set_colors(self, scheme):
282 """Shorthand access to the color table scheme selector method."""
282 """Shorthand access to the color table scheme selector method."""
283 self.color_scheme_table.set_active_scheme(scheme)
283 self.color_scheme_table.set_active_scheme(scheme)
284
284
285 def interaction(self, frame, traceback):
285 def interaction(self, frame, traceback):
286 try:
286 try:
287 OldPdb.interaction(self, frame, traceback)
287 OldPdb.interaction(self, frame, traceback)
288 except KeyboardInterrupt:
288 except KeyboardInterrupt:
289 sys.stdout.write('\n' + self.shell.get_exception_only())
289 sys.stdout.write('\n' + self.shell.get_exception_only())
290
290
291 def parseline(self, line):
291 def parseline(self, line):
292 if line.startswith("!!"):
292 if line.startswith("!!"):
293 # Force standard behavior.
293 # Force standard behavior.
294 return super(Pdb, self).parseline(line[2:])
294 return super(Pdb, self).parseline(line[2:])
295 # "Smart command mode" from pdb++: don't execute commands if a variable
295 # "Smart command mode" from pdb++: don't execute commands if a variable
296 # with the same name exists.
296 # with the same name exists.
297 cmd, arg, newline = super(Pdb, self).parseline(line)
297 cmd, arg, newline = super(Pdb, self).parseline(line)
298 # Fix for #9611: Do not trigger smart command if the command is `exit`
298 # Fix for #9611: Do not trigger smart command if the command is `exit`
299 # or `quit` and it would resolve to their *global* value (the
299 # or `quit` and it would resolve to their *global* value (the
300 # `ExitAutocall` object). Just checking that it is not present in the
300 # `ExitAutocall` object). Just checking that it is not present in the
301 # locals dict is not enough as locals and globals match at the
301 # locals dict is not enough as locals and globals match at the
302 # toplevel.
302 # toplevel.
303 if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
303 if ((cmd in self.curframe.f_locals or cmd in self.curframe.f_globals)
304 and not (cmd in ["exit", "quit"]
304 and not (cmd in ["exit", "quit"]
305 and (self.curframe.f_locals is self.curframe.f_globals
305 and (self.curframe.f_locals is self.curframe.f_globals
306 or cmd not in self.curframe.f_locals))):
306 or cmd not in self.curframe.f_locals))):
307 return super(Pdb, self).parseline("!" + line)
307 return super(Pdb, self).parseline("!" + line)
308 return super(Pdb, self).parseline(line)
308 return super(Pdb, self).parseline(line)
309
309
310 def new_do_up(self, arg):
310 def new_do_up(self, arg):
311 OldPdb.do_up(self, arg)
311 OldPdb.do_up(self, arg)
312 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
312 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
313
313
314 def new_do_down(self, arg):
314 def new_do_down(self, arg):
315 OldPdb.do_down(self, arg)
315 OldPdb.do_down(self, arg)
316
316
317 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
317 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
318
318
319 def new_do_frame(self, arg):
319 def new_do_frame(self, arg):
320 OldPdb.do_frame(self, arg)
320 OldPdb.do_frame(self, arg)
321
321
322 def new_do_quit(self, arg):
322 def new_do_quit(self, arg):
323
323
324 if hasattr(self, 'old_all_completions'):
324 if hasattr(self, 'old_all_completions'):
325 self.shell.Completer.all_completions=self.old_all_completions
325 self.shell.Completer.all_completions=self.old_all_completions
326
326
327 return OldPdb.do_quit(self, arg)
327 return OldPdb.do_quit(self, arg)
328
328
329 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
329 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
330
330
331 def new_do_restart(self, arg):
331 def new_do_restart(self, arg):
332 """Restart command. In the context of ipython this is exactly the same
332 """Restart command. In the context of ipython this is exactly the same
333 thing as 'quit'."""
333 thing as 'quit'."""
334 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
334 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
335 return self.do_quit(arg)
335 return self.do_quit(arg)
336
336
337 def print_stack_trace(self, context=None):
337 def print_stack_trace(self, context=None):
338 if context is None:
338 if context is None:
339 context = self.context
339 context = self.context
340 try:
340 try:
341 context=int(context)
341 context=int(context)
342 if context <= 0:
342 if context <= 0:
343 raise ValueError("Context must be a positive integer")
343 raise ValueError("Context must be a positive integer")
344 except (TypeError, ValueError):
344 except (TypeError, ValueError):
345 raise ValueError("Context must be a positive integer")
345 raise ValueError("Context must be a positive integer")
346 try:
346 try:
347 for frame_lineno in self.stack:
347 for frame_lineno in self.stack:
348 self.print_stack_entry(frame_lineno, context=context)
348 self.print_stack_entry(frame_lineno, context=context)
349 except KeyboardInterrupt:
349 except KeyboardInterrupt:
350 pass
350 pass
351
351
352 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
352 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
353 context=None):
353 context=None):
354 if context is None:
354 if context is None:
355 context = self.context
355 context = self.context
356 try:
356 try:
357 context=int(context)
357 context=int(context)
358 if context <= 0:
358 if context <= 0:
359 raise ValueError("Context must be a positive integer")
359 raise ValueError("Context must be a positive integer")
360 except (TypeError, ValueError):
360 except (TypeError, ValueError):
361 raise ValueError("Context must be a positive integer")
361 raise ValueError("Context must be a positive integer")
362 print(self.format_stack_entry(frame_lineno, '', context))
362 print(self.format_stack_entry(frame_lineno, '', context))
363
363
364 # vds: >>
364 # vds: >>
365 frame, lineno = frame_lineno
365 frame, lineno = frame_lineno
366 filename = frame.f_code.co_filename
366 filename = frame.f_code.co_filename
367 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
367 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
368 # vds: <<
368 # vds: <<
369
369
370 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
370 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
371 if context is None:
371 if context is None:
372 context = self.context
372 context = self.context
373 try:
373 try:
374 context=int(context)
374 context=int(context)
375 if context <= 0:
375 if context <= 0:
376 print("Context must be a positive integer")
376 print("Context must be a positive integer")
377 except (TypeError, ValueError):
377 except (TypeError, ValueError):
378 print("Context must be a positive integer")
378 print("Context must be a positive integer")
379 try:
379 try:
380 import reprlib # Py 3
380 import reprlib # Py 3
381 except ImportError:
381 except ImportError:
382 import repr as reprlib # Py 2
382 import repr as reprlib # Py 2
383
383
384 ret = []
384 ret = []
385
385
386 Colors = self.color_scheme_table.active_colors
386 Colors = self.color_scheme_table.active_colors
387 ColorsNormal = Colors.Normal
387 ColorsNormal = Colors.Normal
388 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
388 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
389 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
389 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
390 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
390 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
391 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
391 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
392 ColorsNormal)
392 ColorsNormal)
393
393
394 frame, lineno = frame_lineno
394 frame, lineno = frame_lineno
395
395
396 return_value = ''
396 return_value = ''
397 if '__return__' in frame.f_locals:
397 if '__return__' in frame.f_locals:
398 rv = frame.f_locals['__return__']
398 rv = frame.f_locals['__return__']
399 #return_value += '->'
399 #return_value += '->'
400 return_value += reprlib.repr(rv) + '\n'
400 return_value += reprlib.repr(rv) + '\n'
401 ret.append(return_value)
401 ret.append(return_value)
402
402
403 #s = filename + '(' + `lineno` + ')'
403 #s = filename + '(' + `lineno` + ')'
404 filename = self.canonic(frame.f_code.co_filename)
404 filename = self.canonic(frame.f_code.co_filename)
405 link = tpl_link % py3compat.cast_unicode(filename)
405 link = tpl_link % py3compat.cast_unicode(filename)
406
406
407 if frame.f_code.co_name:
407 if frame.f_code.co_name:
408 func = frame.f_code.co_name
408 func = frame.f_code.co_name
409 else:
409 else:
410 func = "<lambda>"
410 func = "<lambda>"
411
411
412 call = ''
412 call = ''
413 if func != '?':
413 if func != '?':
414 if '__args__' in frame.f_locals:
414 if '__args__' in frame.f_locals:
415 args = reprlib.repr(frame.f_locals['__args__'])
415 args = reprlib.repr(frame.f_locals['__args__'])
416 else:
416 else:
417 args = '()'
417 args = '()'
418 call = tpl_call % (func, args)
418 call = tpl_call % (func, args)
419
419
420 # The level info should be generated in the same format pdb uses, to
420 # The level info should be generated in the same format pdb uses, to
421 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
421 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
422 if frame is self.curframe:
422 if frame is self.curframe:
423 ret.append('> ')
423 ret.append('> ')
424 else:
424 else:
425 ret.append(' ')
425 ret.append(' ')
426 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
426 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
427
427
428 start = lineno - 1 - context//2
428 start = lineno - 1 - context//2
429 lines = ulinecache.getlines(filename)
429 lines = ulinecache.getlines(filename)
430 start = min(start, len(lines) - context)
430 start = min(start, len(lines) - context)
431 start = max(start, 0)
431 start = max(start, 0)
432 lines = lines[start : start + context]
432 lines = lines[start : start + context]
433
433
434 for i,line in enumerate(lines):
434 for i,line in enumerate(lines):
435 show_arrow = (start + 1 + i == lineno)
435 show_arrow = (start + 1 + i == lineno)
436 linetpl = (frame is self.curframe or show_arrow) \
436 linetpl = (frame is self.curframe or show_arrow) \
437 and tpl_line_em \
437 and tpl_line_em \
438 or tpl_line
438 or tpl_line
439 ret.append(self.__format_line(linetpl, filename,
439 ret.append(self.__format_line(linetpl, filename,
440 start + 1 + i, line,
440 start + 1 + i, line,
441 arrow = show_arrow) )
441 arrow = show_arrow) )
442 return ''.join(ret)
442 return ''.join(ret)
443
443
444 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
444 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
445 bp_mark = ""
445 bp_mark = ""
446 bp_mark_color = ""
446 bp_mark_color = ""
447
447
448 scheme = self.color_scheme_table.active_scheme_name
448 scheme = self.color_scheme_table.active_scheme_name
449 new_line, err = self.parser.format2(line, 'str', scheme)
449 new_line, err = self.parser.format2(line, 'str', scheme)
450 if not err: line = new_line
450 if not err: line = new_line
451
451
452 bp = None
452 bp = None
453 if lineno in self.get_file_breaks(filename):
453 if lineno in self.get_file_breaks(filename):
454 bps = self.get_breaks(filename, lineno)
454 bps = self.get_breaks(filename, lineno)
455 bp = bps[-1]
455 bp = bps[-1]
456
456
457 if bp:
457 if bp:
458 Colors = self.color_scheme_table.active_colors
458 Colors = self.color_scheme_table.active_colors
459 bp_mark = str(bp.number)
459 bp_mark = str(bp.number)
460 bp_mark_color = Colors.breakpoint_enabled
460 bp_mark_color = Colors.breakpoint_enabled
461 if not bp.enabled:
461 if not bp.enabled:
462 bp_mark_color = Colors.breakpoint_disabled
462 bp_mark_color = Colors.breakpoint_disabled
463
463
464 numbers_width = 7
464 numbers_width = 7
465 if arrow:
465 if arrow:
466 # This is the line with the error
466 # This is the line with the error
467 pad = numbers_width - len(str(lineno)) - len(bp_mark)
467 pad = numbers_width - len(str(lineno)) - len(bp_mark)
468 num = '%s%s' % (make_arrow(pad), str(lineno))
468 num = '%s%s' % (make_arrow(pad), str(lineno))
469 else:
469 else:
470 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
470 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
471
471
472 return tpl_line % (bp_mark_color + bp_mark, num, line)
472 return tpl_line % (bp_mark_color + bp_mark, num, line)
473
473
474
474
475 def print_list_lines(self, filename, first, last):
475 def print_list_lines(self, filename, first, last):
476 """The printing (as opposed to the parsing part of a 'list'
476 """The printing (as opposed to the parsing part of a 'list'
477 command."""
477 command."""
478 try:
478 try:
479 Colors = self.color_scheme_table.active_colors
479 Colors = self.color_scheme_table.active_colors
480 ColorsNormal = Colors.Normal
480 ColorsNormal = Colors.Normal
481 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
481 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
482 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
482 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
483 src = []
483 src = []
484 if filename == "<string>" and hasattr(self, "_exec_filename"):
484 if filename == "<string>" and hasattr(self, "_exec_filename"):
485 filename = self._exec_filename
485 filename = self._exec_filename
486
486
487 for lineno in range(first, last+1):
487 for lineno in range(first, last+1):
488 line = ulinecache.getline(filename, lineno)
488 line = ulinecache.getline(filename, lineno)
489 if not line:
489 if not line:
490 break
490 break
491
491
492 if lineno == self.curframe.f_lineno:
492 if lineno == self.curframe.f_lineno:
493 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
493 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
494 else:
494 else:
495 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
495 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
496
496
497 src.append(line)
497 src.append(line)
498 self.lineno = lineno
498 self.lineno = lineno
499
499
500 print(''.join(src))
500 print(''.join(src))
501
501
502 except KeyboardInterrupt:
502 except KeyboardInterrupt:
503 pass
503 pass
504
504
505 def do_list(self, arg):
505 def do_list(self, arg):
506 self.lastcmd = 'list'
506 self.lastcmd = 'list'
507 last = None
507 last = None
508 if arg:
508 if arg:
509 try:
509 try:
510 x = eval(arg, {}, {})
510 x = eval(arg, {}, {})
511 if type(x) == type(()):
511 if type(x) == type(()):
512 first, last = x
512 first, last = x
513 first = int(first)
513 first = int(first)
514 last = int(last)
514 last = int(last)
515 if last < first:
515 if last < first:
516 # Assume it's a count
516 # Assume it's a count
517 last = first + last
517 last = first + last
518 else:
518 else:
519 first = max(1, int(x) - 5)
519 first = max(1, int(x) - 5)
520 except:
520 except:
521 print('*** Error in argument:', repr(arg))
521 print('*** Error in argument:', repr(arg))
522 return
522 return
523 elif self.lineno is None:
523 elif self.lineno is None:
524 first = max(1, self.curframe.f_lineno - 5)
524 first = max(1, self.curframe.f_lineno - 5)
525 else:
525 else:
526 first = self.lineno + 1
526 first = self.lineno + 1
527 if last is None:
527 if last is None:
528 last = first + 10
528 last = first + 10
529 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
529 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
530
530
531 # vds: >>
531 # vds: >>
532 lineno = first
532 lineno = first
533 filename = self.curframe.f_code.co_filename
533 filename = self.curframe.f_code.co_filename
534 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
534 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
535 # vds: <<
535 # vds: <<
536
536
537 do_l = do_list
537 do_l = do_list
538
538
539 def getsourcelines(self, obj):
539 def getsourcelines(self, obj):
540 lines, lineno = inspect.findsource(obj)
540 lines, lineno = inspect.findsource(obj)
541 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
541 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
542 # must be a module frame: do not try to cut a block out of it
542 # must be a module frame: do not try to cut a block out of it
543 return lines, 1
543 return lines, 1
544 elif inspect.ismodule(obj):
544 elif inspect.ismodule(obj):
545 return lines, 1
545 return lines, 1
546 return inspect.getblock(lines[lineno:]), lineno+1
546 return inspect.getblock(lines[lineno:]), lineno+1
547
547
548 def do_longlist(self, arg):
548 def do_longlist(self, arg):
549 self.lastcmd = 'longlist'
549 self.lastcmd = 'longlist'
550 try:
550 try:
551 lines, lineno = self.getsourcelines(self.curframe)
551 lines, lineno = self.getsourcelines(self.curframe)
552 except OSError as err:
552 except OSError as err:
553 self.error(err)
553 self.error(err)
554 return
554 return
555 last = lineno + len(lines)
555 last = lineno + len(lines)
556 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
556 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
557 do_ll = do_longlist
557 do_ll = do_longlist
558
558
559 def do_pdef(self, arg):
559 def do_pdef(self, arg):
560 """Print the call signature for any callable object.
560 """Print the call signature for any callable object.
561
561
562 The debugger interface to %pdef"""
562 The debugger interface to %pdef"""
563 namespaces = [('Locals', self.curframe.f_locals),
563 namespaces = [('Locals', self.curframe.f_locals),
564 ('Globals', self.curframe.f_globals)]
564 ('Globals', self.curframe.f_globals)]
565 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
565 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
566
566
567 def do_pdoc(self, arg):
567 def do_pdoc(self, arg):
568 """Print the docstring for an object.
568 """Print the docstring for an object.
569
569
570 The debugger interface to %pdoc."""
570 The debugger interface to %pdoc."""
571 namespaces = [('Locals', self.curframe.f_locals),
571 namespaces = [('Locals', self.curframe.f_locals),
572 ('Globals', self.curframe.f_globals)]
572 ('Globals', self.curframe.f_globals)]
573 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
573 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
574
574
575 def do_pfile(self, arg):
575 def do_pfile(self, arg):
576 """Print (or run through pager) the file where an object is defined.
576 """Print (or run through pager) the file where an object is defined.
577
577
578 The debugger interface to %pfile.
578 The debugger interface to %pfile.
579 """
579 """
580 namespaces = [('Locals', self.curframe.f_locals),
580 namespaces = [('Locals', self.curframe.f_locals),
581 ('Globals', self.curframe.f_globals)]
581 ('Globals', self.curframe.f_globals)]
582 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
582 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
583
583
584 def do_pinfo(self, arg):
584 def do_pinfo(self, arg):
585 """Provide detailed information about an object.
585 """Provide detailed information about an object.
586
586
587 The debugger interface to %pinfo, i.e., obj?."""
587 The debugger interface to %pinfo, i.e., obj?."""
588 namespaces = [('Locals', self.curframe.f_locals),
588 namespaces = [('Locals', self.curframe.f_locals),
589 ('Globals', self.curframe.f_globals)]
589 ('Globals', self.curframe.f_globals)]
590 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
590 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
591
591
592 def do_pinfo2(self, arg):
592 def do_pinfo2(self, arg):
593 """Provide extra detailed information about an object.
593 """Provide extra detailed information about an object.
594
594
595 The debugger interface to %pinfo2, i.e., obj??."""
595 The debugger interface to %pinfo2, i.e., obj??."""
596 namespaces = [('Locals', self.curframe.f_locals),
596 namespaces = [('Locals', self.curframe.f_locals),
597 ('Globals', self.curframe.f_globals)]
597 ('Globals', self.curframe.f_globals)]
598 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
598 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
599
599
600 def do_psource(self, arg):
600 def do_psource(self, arg):
601 """Print (or run through pager) the source code for an object."""
601 """Print (or run through pager) the source code for an object."""
602 namespaces = [('Locals', self.curframe.f_locals),
602 namespaces = [('Locals', self.curframe.f_locals),
603 ('Globals', self.curframe.f_globals)]
603 ('Globals', self.curframe.f_globals)]
604 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
604 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
605
605
606 if sys.version_info > (3, ):
606 if sys.version_info > (3, ):
607 def do_where(self, arg):
607 def do_where(self, arg):
608 """w(here)
608 """w(here)
609 Print a stack trace, with the most recent frame at the bottom.
609 Print a stack trace, with the most recent frame at the bottom.
610 An arrow indicates the "current frame", which determines the
610 An arrow indicates the "current frame", which determines the
611 context of most commands. 'bt' is an alias for this command.
611 context of most commands. 'bt' is an alias for this command.
612
612
613 Take a number as argument as an (optional) number of context line to
613 Take a number as argument as an (optional) number of context line to
614 print"""
614 print"""
615 if arg:
615 if arg:
616 context = int(arg)
616 context = int(arg)
617 self.print_stack_trace(context)
617 self.print_stack_trace(context)
618 else:
618 else:
619 self.print_stack_trace()
619 self.print_stack_trace()
620
620
621 do_w = do_where
621 do_w = do_where
622
622
623
623
624 def set_trace(frame=None):
624 def set_trace(frame=None):
625 """
625 """
626 Start debugging from `frame`.
626 Start debugging from `frame`.
627
627
628 If frame is not specified, debugging starts from caller's frame.
628 If frame is not specified, debugging starts from caller's frame.
629 """
629 """
630 Pdb().set_trace(frame or sys._getframe().f_back)
630 Pdb().set_trace(frame or sys._getframe().f_back)
@@ -1,176 +1,177 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Color schemes for exception handling code in IPython.
3 Color schemes for exception handling code in IPython.
4 """
4 """
5
5
6 import warnings
6 import warnings
7
7
8 #*****************************************************************************
8 #*****************************************************************************
9 # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu>
9 # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu>
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 from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme
15 from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme
16
16
17 def exception_colors():
17 def exception_colors():
18 """Return a color table with fields for exception reporting.
18 """Return a color table with fields for exception reporting.
19
19
20 The table is an instance of ColorSchemeTable with schemes added for
20 The table is an instance of ColorSchemeTable with schemes added for
21 'Neutral', 'Linux', 'LightBG' and 'NoColor' and fields for exception handling filled
21 'Neutral', 'Linux', 'LightBG' and 'NoColor' and fields for exception handling filled
22 in.
22 in.
23
23
24 Examples:
24 Examples:
25
25
26 >>> ec = exception_colors()
26 >>> ec = exception_colors()
27 >>> ec.active_scheme_name
27 >>> ec.active_scheme_name
28 ''
28 ''
29 >>> print(ec.active_colors)
29 >>> print(ec.active_colors)
30 None
30 None
31
31
32 Now we activate a color scheme:
32 Now we activate a color scheme:
33 >>> ec.set_active_scheme('NoColor')
33 >>> ec.set_active_scheme('NoColor')
34 >>> ec.active_scheme_name
34 >>> ec.active_scheme_name
35 'NoColor'
35 'NoColor'
36 >>> sorted(ec.active_colors.keys())
36 >>> sorted(ec.active_colors.keys())
37 ['Normal', 'caret', 'em', 'excName', 'filename', 'filenameEm', 'line',
37 ['Normal', 'caret', 'em', 'excName', 'filename', 'filenameEm', 'line',
38 'lineno', 'linenoEm', 'name', 'nameEm', 'normalEm', 'topline', 'vName',
38 'lineno', 'linenoEm', 'name', 'nameEm', 'normalEm', 'topline', 'vName',
39 'val', 'valEm']
39 'val', 'valEm']
40 """
40 """
41
41
42 ex_colors = ColorSchemeTable()
42 ex_colors = ColorSchemeTable()
43
43
44 # Populate it with color schemes
44 # Populate it with color schemes
45 C = TermColors # shorthand and local lookup
45 C = TermColors # shorthand and local lookup
46 ex_colors.add_scheme(ColorScheme(
46 ex_colors.add_scheme(ColorScheme(
47 'NoColor',
47 'NoColor',
48 # The color to be used for the top line
48 # The color to be used for the top line
49 topline = C.NoColor,
49 topline = C.NoColor,
50
50
51 # The colors to be used in the traceback
51 # The colors to be used in the traceback
52 filename = C.NoColor,
52 filename = C.NoColor,
53 lineno = C.NoColor,
53 lineno = C.NoColor,
54 name = C.NoColor,
54 name = C.NoColor,
55 vName = C.NoColor,
55 vName = C.NoColor,
56 val = C.NoColor,
56 val = C.NoColor,
57 em = C.NoColor,
57 em = C.NoColor,
58
58
59 # Emphasized colors for the last frame of the traceback
59 # Emphasized colors for the last frame of the traceback
60 normalEm = C.NoColor,
60 normalEm = C.NoColor,
61 filenameEm = C.NoColor,
61 filenameEm = C.NoColor,
62 linenoEm = C.NoColor,
62 linenoEm = C.NoColor,
63 nameEm = C.NoColor,
63 nameEm = C.NoColor,
64 valEm = C.NoColor,
64 valEm = C.NoColor,
65
65
66 # Colors for printing the exception
66 # Colors for printing the exception
67 excName = C.NoColor,
67 excName = C.NoColor,
68 line = C.NoColor,
68 line = C.NoColor,
69 caret = C.NoColor,
69 caret = C.NoColor,
70 Normal = C.NoColor
70 Normal = C.NoColor
71 ))
71 ))
72
72
73 # make some schemes as instances so we can copy them for modification easily
73 # make some schemes as instances so we can copy them for modification easily
74 ex_colors.add_scheme(ColorScheme(
74 ex_colors.add_scheme(ColorScheme(
75 'Linux',
75 'Linux',
76 # The color to be used for the top line
76 # The color to be used for the top line
77 topline = C.LightRed,
77 topline = C.LightRed,
78
78
79 # The colors to be used in the traceback
79 # The colors to be used in the traceback
80 filename = C.Green,
80 filename = C.Green,
81 lineno = C.Green,
81 lineno = C.Green,
82 name = C.Purple,
82 name = C.Purple,
83 vName = C.Cyan,
83 vName = C.Cyan,
84 val = C.Green,
84 val = C.Green,
85 em = C.LightCyan,
85 em = C.LightCyan,
86
86
87 # Emphasized colors for the last frame of the traceback
87 # Emphasized colors for the last frame of the traceback
88 normalEm = C.LightCyan,
88 normalEm = C.LightCyan,
89 filenameEm = C.LightGreen,
89 filenameEm = C.LightGreen,
90 linenoEm = C.LightGreen,
90 linenoEm = C.LightGreen,
91 nameEm = C.LightPurple,
91 nameEm = C.LightPurple,
92 valEm = C.LightBlue,
92 valEm = C.LightBlue,
93
93
94 # Colors for printing the exception
94 # Colors for printing the exception
95 excName = C.LightRed,
95 excName = C.LightRed,
96 line = C.Yellow,
96 line = C.Yellow,
97 caret = C.White,
97 caret = C.White,
98 Normal = C.Normal
98 Normal = C.Normal
99 ))
99 ))
100
100
101 # For light backgrounds, swap dark/light colors
101 # For light backgrounds, swap dark/light colors
102 ex_colors.add_scheme(ColorScheme(
102 ex_colors.add_scheme(ColorScheme(
103 'LightBG',
103 'LightBG',
104 # The color to be used for the top line
104 # The color to be used for the top line
105 topline = C.Red,
105 topline = C.Red,
106
106
107 # The colors to be used in the traceback
107 # The colors to be used in the traceback
108 filename = C.LightGreen,
108 filename = C.LightGreen,
109 lineno = C.LightGreen,
109 lineno = C.LightGreen,
110 name = C.LightPurple,
110 name = C.LightPurple,
111 vName = C.Cyan,
111 vName = C.Cyan,
112 val = C.LightGreen,
112 val = C.LightGreen,
113 em = C.Cyan,
113 em = C.Cyan,
114
114
115 # Emphasized colors for the last frame of the traceback
115 # Emphasized colors for the last frame of the traceback
116 normalEm = C.Cyan,
116 normalEm = C.Cyan,
117 filenameEm = C.Green,
117 filenameEm = C.Green,
118 linenoEm = C.Green,
118 linenoEm = C.Green,
119 nameEm = C.Purple,
119 nameEm = C.Purple,
120 valEm = C.Blue,
120 valEm = C.Blue,
121
121
122 # Colors for printing the exception
122 # Colors for printing the exception
123 excName = C.Red,
123 excName = C.Red,
124 #line = C.Brown, # brown often is displayed as yellow
124 #line = C.Brown, # brown often is displayed as yellow
125 line = C.Red,
125 line = C.Red,
126 caret = C.Normal,
126 caret = C.Normal,
127 Normal = C.Normal,
127 Normal = C.Normal,
128 ))
128 ))
129
129
130 ex_colors.add_scheme(ColorScheme(
130 ex_colors.add_scheme(ColorScheme(
131 'Neutral',
131 'Neutral',
132 # The color to be used for the top line
132 # The color to be used for the top line
133 topline = C.Red,
133 topline = C.Red,
134
134
135 # The colors to be used in the traceback
135 # The colors to be used in the traceback
136 filename = C.LightGreen,
136 filename = C.LightGreen,
137 lineno = C.LightGreen,
137 lineno = C.LightGreen,
138 name = C.LightPurple,
138 name = C.LightPurple,
139 vName = C.Cyan,
139 vName = C.Cyan,
140 val = C.LightGreen,
140 val = C.LightGreen,
141 em = C.Cyan,
141 em = C.Cyan,
142
142
143 # Emphasized colors for the last frame of the traceback
143 # Emphasized colors for the last frame of the traceback
144 normalEm = C.Cyan,
144 normalEm = C.Cyan,
145 filenameEm = C.Green,
145 filenameEm = C.Green,
146 linenoEm = C.Green,
146 linenoEm = C.Green,
147 nameEm = C.Purple,
147 nameEm = C.Purple,
148 valEm = C.Blue,
148 valEm = C.Blue,
149
149
150 # Colors for printing the exception
150 # Colors for printing the exception
151 excName = C.Red,
151 excName = C.Red,
152 #line = C.Brown, # brown often is displayed as yellow
152 #line = C.Brown, # brown often is displayed as yellow
153 line = C.Red,
153 line = C.Red,
154 caret = C.Normal,
154 caret = C.Normal,
155 Normal = C.Normal,
155 Normal = C.Normal,
156 ))
156 ))
157
157
158
158
159 return ex_colors
159 return ex_colors
160
160
161 class Deprec(object):
161 class Deprec(object):
162
162
163 def __init__(self, wrapped_obj):
163 def __init__(self, wrapped_obj):
164 self.wrapped=wrapped_obj
164 self.wrapped=wrapped_obj
165
165
166 def __getattr__(self, name):
166 def __getattr__(self, name):
167 val = getattr(self.wrapped, name)
167 val = getattr(self.wrapped, name)
168 warnings.warn("Using ExceptionColors global is deprecated and will be removed in IPython 6.0", DeprecationWarning)
168 warnings.warn("Using ExceptionColors global is deprecated and will be removed in IPython 6.0",
169 DeprecationWarning, stacklevel=2)
169 # using getattr after warnings break ipydoctest in weird way for 3.5
170 # using getattr after warnings break ipydoctest in weird way for 3.5
170 return val
171 return val
171
172
172 # For backwards compatibility, keep around a single global object. Note that
173 # For backwards compatibility, keep around a single global object. Note that
173 # this should NOT be used, the factory function should be used instead, since
174 # this should NOT be used, the factory function should be used instead, since
174 # these objects are stateful and it's very easy to get strange bugs if any code
175 # these objects are stateful and it's very easy to get strange bugs if any code
175 # modifies the module-level object's state.
176 # modifies the module-level object's state.
176 ExceptionColors = Deprec(exception_colors())
177 ExceptionColors = Deprec(exception_colors())
@@ -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,380 +1,384 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Decorators for labeling test objects.
2 """Decorators for labeling test objects.
3
3
4 Decorators that merely return a modified version of the original function
4 Decorators that merely return a modified version of the original function
5 object are straightforward. Decorators that return a new function object need
5 object are straightforward. Decorators that return a new function object need
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
6 to use nose.tools.make_decorator(original_function)(decorator) in returning the
7 decorator, in order to preserve metadata such as function name, setup and
7 decorator, in order to preserve metadata such as function name, setup and
8 teardown functions and so on - see nose.tools for more information.
8 teardown functions and so on - see nose.tools for more information.
9
9
10 This module provides a set of useful decorators meant to be ready to use in
10 This module provides a set of useful decorators meant to be ready to use in
11 your own tests. See the bottom of the file for the ready-made ones, and if you
11 your own tests. See the bottom of the file for the ready-made ones, and if you
12 find yourself writing a new one that may be of generic use, add it here.
12 find yourself writing a new one that may be of generic use, add it here.
13
13
14 Included decorators:
14 Included decorators:
15
15
16
16
17 Lightweight testing that remains unittest-compatible.
17 Lightweight testing that remains unittest-compatible.
18
18
19 - An @as_unittest decorator can be used to tag any normal parameter-less
19 - An @as_unittest decorator can be used to tag any normal parameter-less
20 function as a unittest TestCase. Then, both nose and normal unittest will
20 function as a unittest TestCase. Then, both nose and normal unittest will
21 recognize it as such. This will make it easier to migrate away from Nose if
21 recognize it as such. This will make it easier to migrate away from Nose if
22 we ever need/want to while maintaining very lightweight tests.
22 we ever need/want to while maintaining very lightweight tests.
23
23
24 NOTE: This file contains IPython-specific decorators. Using the machinery in
24 NOTE: This file contains IPython-specific decorators. Using the machinery in
25 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
25 IPython.external.decorators, we import either numpy.testing.decorators if numpy is
26 available, OR use equivalent code in IPython.external._decorators, which
26 available, OR use equivalent code in IPython.external._decorators, which
27 we've copied verbatim from numpy.
27 we've copied verbatim from numpy.
28
28
29 """
29 """
30
30
31 # Copyright (c) IPython Development Team.
31 # Copyright (c) IPython Development Team.
32 # Distributed under the terms of the Modified BSD License.
32 # Distributed under the terms of the Modified BSD License.
33
33
34 import sys
34 import sys
35 import os
35 import os
36 import tempfile
36 import tempfile
37 import unittest
37 import unittest
38 import warnings
38 import warnings
39
39
40 from decorator import decorator
40 from decorator import decorator
41
41
42 # Expose the unittest-driven decorators
42 # Expose the unittest-driven decorators
43 from .ipunittest import ipdoctest, ipdocstring
43 from .ipunittest import ipdoctest, ipdocstring
44
44
45 # Grab the numpy-specific decorators which we keep in a file that we
45 # Grab the numpy-specific decorators which we keep in a file that we
46 # occasionally update from upstream: decorators.py is a copy of
46 # occasionally update from upstream: decorators.py is a copy of
47 # numpy.testing.decorators, we expose all of it here.
47 # numpy.testing.decorators, we expose all of it here.
48 from IPython.external.decorators import *
48 from IPython.external.decorators import *
49
49
50 # For onlyif_cmd_exists decorator
50 # For onlyif_cmd_exists decorator
51 from IPython.utils.py3compat import string_types, which, PY2, PY3, PYPY
51 from IPython.utils.py3compat import string_types, which, PY2, PY3, PYPY
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Classes and functions
54 # Classes and functions
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 # Simple example of the basic idea
57 # Simple example of the basic idea
58 def as_unittest(func):
58 def as_unittest(func):
59 """Decorator to make a simple function into a normal test via unittest."""
59 """Decorator to make a simple function into a normal test via unittest."""
60 class Tester(unittest.TestCase):
60 class Tester(unittest.TestCase):
61 def test(self):
61 def test(self):
62 func()
62 func()
63
63
64 Tester.__name__ = func.__name__
64 Tester.__name__ = func.__name__
65
65
66 return Tester
66 return Tester
67
67
68 # Utility functions
68 # Utility functions
69
69
70 def apply_wrapper(wrapper,func):
70 def apply_wrapper(wrapper, func):
71 """Apply a wrapper to a function for decoration.
71 """Apply a wrapper to a function for decoration.
72
72
73 This mixes Michele Simionato's decorator tool with nose's make_decorator,
73 This mixes Michele Simionato's decorator tool with nose's make_decorator,
74 to apply a wrapper in a decorator so that all nose attributes, as well as
74 to apply a wrapper in a decorator so that all nose attributes, as well as
75 function signature and other properties, survive the decoration cleanly.
75 function signature and other properties, survive the decoration cleanly.
76 This will ensure that wrapped functions can still be well introspected via
76 This will ensure that wrapped functions can still be well introspected via
77 IPython, for example.
77 IPython, for example.
78 """
78 """
79 warnings.warn("The function `apply_wrapper` is deprecated and might be removed in IPython 5.0", DeprecationWarning)
79 warnings.warn("The function `apply_wrapper` is deprecated since IPython 4.0",
80
80 DeprecationWarning, stacklevel=2)
81 import nose.tools
81 import nose.tools
82
82
83 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
83 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
84
84
85
85
86 def make_label_dec(label,ds=None):
86 def make_label_dec(label, ds=None):
87 """Factory function to create a decorator that applies one or more labels.
87 """Factory function to create a decorator that applies one or more labels.
88
88
89 Parameters
89 Parameters
90 ----------
90 ----------
91 label : string or sequence
91 label : string or sequence
92 One or more labels that will be applied by the decorator to the functions
92 One or more labels that will be applied by the decorator to the functions
93 it decorates. Labels are attributes of the decorated function with their
93 it decorates. Labels are attributes of the decorated function with their
94 value set to True.
94 value set to True.
95
95
96 ds : string
96 ds : string
97 An optional docstring for the resulting decorator. If not given, a
97 An optional docstring for the resulting decorator. If not given, a
98 default docstring is auto-generated.
98 default docstring is auto-generated.
99
99
100 Returns
100 Returns
101 -------
101 -------
102 A decorator.
102 A decorator.
103
103
104 Examples
104 Examples
105 --------
105 --------
106
106
107 A simple labeling decorator:
107 A simple labeling decorator:
108
108
109 >>> slow = make_label_dec('slow')
109 >>> slow = make_label_dec('slow')
110 >>> slow.__doc__
110 >>> slow.__doc__
111 "Labels a test as 'slow'."
111 "Labels a test as 'slow'."
112
112
113 And one that uses multiple labels and a custom docstring:
113 And one that uses multiple labels and a custom docstring:
114
114
115 >>> rare = make_label_dec(['slow','hard'],
115 >>> rare = make_label_dec(['slow','hard'],
116 ... "Mix labels 'slow' and 'hard' for rare tests.")
116 ... "Mix labels 'slow' and 'hard' for rare tests.")
117 >>> rare.__doc__
117 >>> rare.__doc__
118 "Mix labels 'slow' and 'hard' for rare tests."
118 "Mix labels 'slow' and 'hard' for rare tests."
119
119
120 Now, let's test using this one:
120 Now, let's test using this one:
121 >>> @rare
121 >>> @rare
122 ... def f(): pass
122 ... def f(): pass
123 ...
123 ...
124 >>>
124 >>>
125 >>> f.slow
125 >>> f.slow
126 True
126 True
127 >>> f.hard
127 >>> f.hard
128 True
128 True
129 """
129 """
130
130
131 warnings.warn("The function `make_label_dec` is deprecated and might be removed in IPython 5.0", DeprecationWarning)
131 warnings.warn("The function `make_label_dec` is deprecated since IPython 4.0",
132 DeprecationWarning, stacklevel=2)
132 if isinstance(label, string_types):
133 if isinstance(label, string_types):
133 labels = [label]
134 labels = [label]
134 else:
135 else:
135 labels = label
136 labels = label
136
137
137 # Validate that the given label(s) are OK for use in setattr() by doing a
138 # Validate that the given label(s) are OK for use in setattr() by doing a
138 # dry run on a dummy function.
139 # dry run on a dummy function.
139 tmp = lambda : None
140 tmp = lambda : None
140 for label in labels:
141 for label in labels:
141 setattr(tmp,label,True)
142 setattr(tmp,label,True)
142
143
143 # This is the actual decorator we'll return
144 # This is the actual decorator we'll return
144 def decor(f):
145 def decor(f):
145 for label in labels:
146 for label in labels:
146 setattr(f,label,True)
147 setattr(f,label,True)
147 return f
148 return f
148
149
149 # Apply the user's docstring, or autogenerate a basic one
150 # Apply the user's docstring, or autogenerate a basic one
150 if ds is None:
151 if ds is None:
151 ds = "Labels a test as %r." % label
152 ds = "Labels a test as %r." % label
152 decor.__doc__ = ds
153 decor.__doc__ = ds
153
154
154 return decor
155 return decor
155
156
156
157
157 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
158 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
158 # preserve function metadata better and allows the skip condition to be a
159 # preserve function metadata better and allows the skip condition to be a
159 # callable.
160 # callable.
160 def skipif(skip_condition, msg=None):
161 def skipif(skip_condition, msg=None):
161 ''' Make function raise SkipTest exception if skip_condition is true
162 ''' Make function raise SkipTest exception if skip_condition is true
162
163
163 Parameters
164 Parameters
164 ----------
165 ----------
165
166
166 skip_condition : bool or callable
167 skip_condition : bool or callable
167 Flag to determine whether to skip test. If the condition is a
168 Flag to determine whether to skip test. If the condition is a
168 callable, it is used at runtime to dynamically make the decision. This
169 callable, it is used at runtime to dynamically make the decision. This
169 is useful for tests that may require costly imports, to delay the cost
170 is useful for tests that may require costly imports, to delay the cost
170 until the test suite is actually executed.
171 until the test suite is actually executed.
171 msg : string
172 msg : string
172 Message to give on raising a SkipTest exception.
173 Message to give on raising a SkipTest exception.
173
174
174 Returns
175 Returns
175 -------
176 -------
176 decorator : function
177 decorator : function
177 Decorator, which, when applied to a function, causes SkipTest
178 Decorator, which, when applied to a function, causes SkipTest
178 to be raised when the skip_condition was True, and the function
179 to be raised when the skip_condition was True, and the function
179 to be called normally otherwise.
180 to be called normally otherwise.
180
181
181 Notes
182 Notes
182 -----
183 -----
183 You will see from the code that we had to further decorate the
184 You will see from the code that we had to further decorate the
184 decorator with the nose.tools.make_decorator function in order to
185 decorator with the nose.tools.make_decorator function in order to
185 transmit function name, and various other metadata.
186 transmit function name, and various other metadata.
186 '''
187 '''
187
188
188 def skip_decorator(f):
189 def skip_decorator(f):
189 # Local import to avoid a hard nose dependency and only incur the
190 # Local import to avoid a hard nose dependency and only incur the
190 # import time overhead at actual test-time.
191 # import time overhead at actual test-time.
191 import nose
192 import nose
192
193
193 # Allow for both boolean or callable skip conditions.
194 # Allow for both boolean or callable skip conditions.
194 if callable(skip_condition):
195 if callable(skip_condition):
195 skip_val = skip_condition
196 skip_val = skip_condition
196 else:
197 else:
197 skip_val = lambda : skip_condition
198 skip_val = lambda : skip_condition
198
199
199 def get_msg(func,msg=None):
200 def get_msg(func,msg=None):
200 """Skip message with information about function being skipped."""
201 """Skip message with information about function being skipped."""
201 if msg is None: out = 'Test skipped due to test condition.'
202 if msg is None: out = 'Test skipped due to test condition.'
202 else: out = msg
203 else: out = msg
203 return "Skipping test: %s. %s" % (func.__name__,out)
204 return "Skipping test: %s. %s" % (func.__name__,out)
204
205
205 # We need to define *two* skippers because Python doesn't allow both
206 # We need to define *two* skippers because Python doesn't allow both
206 # return with value and yield inside the same function.
207 # return with value and yield inside the same function.
207 def skipper_func(*args, **kwargs):
208 def skipper_func(*args, **kwargs):
208 """Skipper for normal test functions."""
209 """Skipper for normal test functions."""
209 if skip_val():
210 if skip_val():
210 raise nose.SkipTest(get_msg(f,msg))
211 raise nose.SkipTest(get_msg(f,msg))
211 else:
212 else:
212 return f(*args, **kwargs)
213 return f(*args, **kwargs)
213
214
214 def skipper_gen(*args, **kwargs):
215 def skipper_gen(*args, **kwargs):
215 """Skipper for test generators."""
216 """Skipper for test generators."""
216 if skip_val():
217 if skip_val():
217 raise nose.SkipTest(get_msg(f,msg))
218 raise nose.SkipTest(get_msg(f,msg))
218 else:
219 else:
219 for x in f(*args, **kwargs):
220 for x in f(*args, **kwargs):
220 yield x
221 yield x
221
222
222 # Choose the right skipper to use when building the actual generator.
223 # Choose the right skipper to use when building the actual generator.
223 if nose.util.isgenerator(f):
224 if nose.util.isgenerator(f):
224 skipper = skipper_gen
225 skipper = skipper_gen
225 else:
226 else:
226 skipper = skipper_func
227 skipper = skipper_func
227
228
228 return nose.tools.make_decorator(f)(skipper)
229 return nose.tools.make_decorator(f)(skipper)
229
230
230 return skip_decorator
231 return skip_decorator
231
232
232 # A version with the condition set to true, common case just to attach a message
233 # A version with the condition set to true, common case just to attach a message
233 # to a skip decorator
234 # to a skip decorator
234 def skip(msg=None):
235 def skip(msg=None):
235 """Decorator factory - mark a test function for skipping from test suite.
236 """Decorator factory - mark a test function for skipping from test suite.
236
237
237 Parameters
238 Parameters
238 ----------
239 ----------
239 msg : string
240 msg : string
240 Optional message to be added.
241 Optional message to be added.
241
242
242 Returns
243 Returns
243 -------
244 -------
244 decorator : function
245 decorator : function
245 Decorator, which, when applied to a function, causes SkipTest
246 Decorator, which, when applied to a function, causes SkipTest
246 to be raised, with the optional message added.
247 to be raised, with the optional message added.
247 """
248 """
248
249
249 return skipif(True,msg)
250 return skipif(True,msg)
250
251
251
252
252 def onlyif(condition, msg):
253 def onlyif(condition, msg):
253 """The reverse from skipif, see skipif for details."""
254 """The reverse from skipif, see skipif for details."""
254
255
255 if callable(condition):
256 if callable(condition):
256 skip_condition = lambda : not condition()
257 skip_condition = lambda : not condition()
257 else:
258 else:
258 skip_condition = lambda : not condition
259 skip_condition = lambda : not condition
259
260
260 return skipif(skip_condition, msg)
261 return skipif(skip_condition, msg)
261
262
262 #-----------------------------------------------------------------------------
263 #-----------------------------------------------------------------------------
263 # Utility functions for decorators
264 # Utility functions for decorators
264 def module_not_available(module):
265 def module_not_available(module):
265 """Can module be imported? Returns true if module does NOT import.
266 """Can module be imported? Returns true if module does NOT import.
266
267
267 This is used to make a decorator to skip tests that require module to be
268 This is used to make a decorator to skip tests that require module to be
268 available, but delay the 'import numpy' to test execution time.
269 available, but delay the 'import numpy' to test execution time.
269 """
270 """
270 try:
271 try:
271 mod = __import__(module)
272 mod = __import__(module)
272 mod_not_avail = False
273 mod_not_avail = False
273 except ImportError:
274 except ImportError:
274 mod_not_avail = True
275 mod_not_avail = True
275
276
276 return mod_not_avail
277 return mod_not_avail
277
278
278
279
279 def decorated_dummy(dec, name):
280 def decorated_dummy(dec, name):
280 """Return a dummy function decorated with dec, with the given name.
281 """Return a dummy function decorated with dec, with the given name.
281
282
282 Examples
283 Examples
283 --------
284 --------
284 import IPython.testing.decorators as dec
285 import IPython.testing.decorators as dec
285 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
286 setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__)
286 """
287 """
287 warnings.warn("The function `make_label_dec` is deprecated and might be removed in IPython 5.0", DeprecationWarning)
288 warnings.warn("The function `decorated_dummy` is deprecated since IPython 4.0",
289 DeprecationWarning, stacklevel=2)
288 dummy = lambda: None
290 dummy = lambda: None
289 dummy.__name__ = name
291 dummy.__name__ = name
290 return dec(dummy)
292 return dec(dummy)
291
293
292 #-----------------------------------------------------------------------------
294 #-----------------------------------------------------------------------------
293 # Decorators for public use
295 # Decorators for public use
294
296
295 # Decorators to skip certain tests on specific platforms.
297 # Decorators to skip certain tests on specific platforms.
296 skip_win32 = skipif(sys.platform == 'win32',
298 skip_win32 = skipif(sys.platform == 'win32',
297 "This test does not run under Windows")
299 "This test does not run under Windows")
298 skip_linux = skipif(sys.platform.startswith('linux'),
300 skip_linux = skipif(sys.platform.startswith('linux'),
299 "This test does not run under Linux")
301 "This test does not run under Linux")
300 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
302 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
301
303
302
304
303 # Decorators to skip tests if not on specific platforms.
305 # Decorators to skip tests if not on specific platforms.
304 skip_if_not_win32 = skipif(sys.platform != 'win32',
306 skip_if_not_win32 = skipif(sys.platform != 'win32',
305 "This test only runs under Windows")
307 "This test only runs under Windows")
306 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
308 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
307 "This test only runs under Linux")
309 "This test only runs under Linux")
308 skip_if_not_osx = skipif(sys.platform != 'darwin',
310 skip_if_not_osx = skipif(sys.platform != 'darwin',
309 "This test only runs under OSX")
311 "This test only runs under OSX")
310
312
311
313
312 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
314 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
313 os.environ.get('DISPLAY', '') == '')
315 os.environ.get('DISPLAY', '') == '')
314 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
316 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
315
317
316 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
318 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
317
319
318 # not a decorator itself, returns a dummy function to be used as setup
320 # not a decorator itself, returns a dummy function to be used as setup
319 def skip_file_no_x11(name):
321 def skip_file_no_x11(name):
320 warnings.warn("The function `skip_file_no_x11` is deprecated and might be removed in IPython 5.0", DeprecationWarning)
322 warnings.warn("The function `skip_file_no_x11` is deprecated since IPython 4.0",
323 DeprecationWarning, stacklevel=2)
321 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
324 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
322
325
323 # Other skip decorators
326 # Other skip decorators
324
327
325 # generic skip without module
328 # generic skip without module
326 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
329 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
327
330
328 skipif_not_numpy = skip_without('numpy')
331 skipif_not_numpy = skip_without('numpy')
329
332
330 skipif_not_matplotlib = skip_without('matplotlib')
333 skipif_not_matplotlib = skip_without('matplotlib')
331
334
332 skipif_not_sympy = skip_without('sympy')
335 skipif_not_sympy = skip_without('sympy')
333
336
334 skip_known_failure = knownfailureif(True,'This test is known to fail')
337 skip_known_failure = knownfailureif(True,'This test is known to fail')
335
338
336 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
339 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
337 'This test is known to fail on Python 3.')
340 'This test is known to fail on Python 3.')
338
341
339 cpython2_only = skipif(PY3 or PYPY, "This test only runs on CPython 2.")
342 cpython2_only = skipif(PY3 or PYPY, "This test only runs on CPython 2.")
340 py2_only = skipif(PY3, "This test only runs on Python 2.")
343 py2_only = skipif(PY3, "This test only runs on Python 2.")
341 py3_only = skipif(PY2, "This test only runs on Python 3.")
344 py3_only = skipif(PY2, "This test only runs on Python 3.")
342
345
343 # A null 'decorator', useful to make more readable code that needs to pick
346 # A null 'decorator', useful to make more readable code that needs to pick
344 # between different decorators based on OS or other conditions
347 # between different decorators based on OS or other conditions
345 null_deco = lambda f: f
348 null_deco = lambda f: f
346
349
347 # Some tests only run where we can use unicode paths. Note that we can't just
350 # Some tests only run where we can use unicode paths. Note that we can't just
348 # check os.path.supports_unicode_filenames, which is always False on Linux.
351 # check os.path.supports_unicode_filenames, which is always False on Linux.
349 try:
352 try:
350 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
353 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
351 except UnicodeEncodeError:
354 except UnicodeEncodeError:
352 unicode_paths = False
355 unicode_paths = False
353 else:
356 else:
354 unicode_paths = True
357 unicode_paths = True
355 f.close()
358 f.close()
356
359
357 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
360 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
358 "where we can use unicode in filenames."))
361 "where we can use unicode in filenames."))
359
362
360
363
361 def onlyif_cmds_exist(*commands):
364 def onlyif_cmds_exist(*commands):
362 """
365 """
363 Decorator to skip test when at least one of `commands` is not found.
366 Decorator to skip test when at least one of `commands` is not found.
364 """
367 """
365 for cmd in commands:
368 for cmd in commands:
366 if not which(cmd):
369 if not which(cmd):
367 return skip("This test runs only if command '{0}' "
370 return skip("This test runs only if command '{0}' "
368 "is installed".format(cmd))
371 "is installed".format(cmd))
369 return null_deco
372 return null_deco
370
373
371 def onlyif_any_cmd_exists(*commands):
374 def onlyif_any_cmd_exists(*commands):
372 """
375 """
373 Decorator to skip test unless at least one of `commands` is found.
376 Decorator to skip test unless at least one of `commands` is found.
374 """
377 """
375 warnings.warn("The function `onlyif_any_cmd_exists` is deprecated and might be removed in IPython 5.0", DeprecationWarning)
378 warnings.warn("The function `onlyif_any_cmd_exists` is deprecated since IPython 4.0",
379 DeprecationWarning, stacklevel=2)
376 for cmd in commands:
380 for cmd in commands:
377 if which(cmd):
381 if which(cmd):
378 return null_deco
382 return null_deco
379 return skip("This test runs only if one of the commands {0} "
383 return skip("This test runs only if one of the commands {0} "
380 "is installed".format(commands))
384 "is installed".format(commands))
@@ -1,433 +1,443 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 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import glob
22 import glob
23 from io import BytesIO
23 from io import BytesIO
24 import os
24 import os
25 import os.path as path
25 import os.path as path
26 import sys
26 import sys
27 from threading import Thread, Lock, Event
27 from threading import Thread, Lock, Event
28 import warnings
28 import warnings
29
29
30 import nose.plugins.builtin
30 import nose.plugins.builtin
31 from nose.plugins.xunit import Xunit
31 from nose.plugins.xunit import Xunit
32 from nose import SkipTest
32 from nose import SkipTest
33 from nose.core import TestProgram
33 from nose.core import TestProgram
34 from nose.plugins import Plugin
34 from nose.plugins import Plugin
35 from nose.util import safe_str
35 from nose.util import safe_str
36
36
37 from IPython import version_info
37 from IPython import version_info
38 from IPython.utils.py3compat import bytes_to_str
38 from IPython.utils.py3compat import bytes_to_str
39 from IPython.utils.importstring import import_item
39 from IPython.utils.importstring import import_item
40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
41 from IPython.external.decorators import KnownFailure, knownfailureif
41 from IPython.external.decorators import KnownFailure, knownfailureif
42
42
43 pjoin = path.join
43 pjoin = path.join
44
44
45
45
46 # Enable printing all warnings raise by IPython's modules
46 # Enable printing all warnings raise by IPython's modules
47 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
47 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
48 if sys.version_info > (3,0):
48 if sys.version_info > (3,0):
49 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
49 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
50 warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
50 warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
51 warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
51 warnings.filterwarnings('default', message='.*', category=Warning, module='IPy.*')
52
52
53 warnings.filterwarnings('error', message='.*apply_wrapper.*', category=DeprecationWarning, module='.*')
54 warnings.filterwarnings('error', message='.*make_label_dec', category=DeprecationWarning, module='.*')
55 warnings.filterwarnings('error', message='.*decorated_dummy.*', category=DeprecationWarning, module='.*')
56 warnings.filterwarnings('error', message='.*skip_file_no_x11.*', category=DeprecationWarning, module='.*')
57 warnings.filterwarnings('error', message='.*onlyif_any_cmd_exists.*', category=DeprecationWarning, module='.*')
58
59 warnings.filterwarnings('error', message='.*disable_gui.*', category=DeprecationWarning, module='.*')
60
61 warnings.filterwarnings('error', message='.*ExceptionColors global is deprecated.*', category=DeprecationWarning, module='.*')
62
53 if version_info < (6,):
63 if version_info < (6,):
54 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
64 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
55 # warning with the runner they also import from standard import library. (as of Dec 2015)
65 # warning with the runner they also import from standard import library. (as of Dec 2015)
56 # Ignore, let's revisit that in a couple of years for IPython 6.
66 # Ignore, let's revisit that in a couple of years for IPython 6.
57 warnings.filterwarnings('ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
67 warnings.filterwarnings('ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
58
68
59
69
60 # ------------------------------------------------------------------------------
70 # ------------------------------------------------------------------------------
61 # Monkeypatch Xunit to count known failures as skipped.
71 # Monkeypatch Xunit to count known failures as skipped.
62 # ------------------------------------------------------------------------------
72 # ------------------------------------------------------------------------------
63 def monkeypatch_xunit():
73 def monkeypatch_xunit():
64 try:
74 try:
65 knownfailureif(True)(lambda: None)()
75 knownfailureif(True)(lambda: None)()
66 except Exception as e:
76 except Exception as e:
67 KnownFailureTest = type(e)
77 KnownFailureTest = type(e)
68
78
69 def addError(self, test, err, capt=None):
79 def addError(self, test, err, capt=None):
70 if issubclass(err[0], KnownFailureTest):
80 if issubclass(err[0], KnownFailureTest):
71 err = (SkipTest,) + err[1:]
81 err = (SkipTest,) + err[1:]
72 return self.orig_addError(test, err, capt)
82 return self.orig_addError(test, err, capt)
73
83
74 Xunit.orig_addError = Xunit.addError
84 Xunit.orig_addError = Xunit.addError
75 Xunit.addError = addError
85 Xunit.addError = addError
76
86
77 #-----------------------------------------------------------------------------
87 #-----------------------------------------------------------------------------
78 # Check which dependencies are installed and greater than minimum version.
88 # Check which dependencies are installed and greater than minimum version.
79 #-----------------------------------------------------------------------------
89 #-----------------------------------------------------------------------------
80 def extract_version(mod):
90 def extract_version(mod):
81 return mod.__version__
91 return mod.__version__
82
92
83 def test_for(item, min_version=None, callback=extract_version):
93 def test_for(item, min_version=None, callback=extract_version):
84 """Test to see if item is importable, and optionally check against a minimum
94 """Test to see if item is importable, and optionally check against a minimum
85 version.
95 version.
86
96
87 If min_version is given, the default behavior is to check against the
97 If min_version is given, the default behavior is to check against the
88 `__version__` attribute of the item, but specifying `callback` allows you to
98 `__version__` attribute of the item, but specifying `callback` allows you to
89 extract the value you are interested in. e.g::
99 extract the value you are interested in. e.g::
90
100
91 In [1]: import sys
101 In [1]: import sys
92
102
93 In [2]: from IPython.testing.iptest import test_for
103 In [2]: from IPython.testing.iptest import test_for
94
104
95 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
105 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
96 Out[3]: True
106 Out[3]: True
97
107
98 """
108 """
99 try:
109 try:
100 check = import_item(item)
110 check = import_item(item)
101 except (ImportError, RuntimeError):
111 except (ImportError, RuntimeError):
102 # GTK reports Runtime error if it can't be initialized even if it's
112 # GTK reports Runtime error if it can't be initialized even if it's
103 # importable.
113 # importable.
104 return False
114 return False
105 else:
115 else:
106 if min_version:
116 if min_version:
107 if callback:
117 if callback:
108 # extra processing step to get version to compare
118 # extra processing step to get version to compare
109 check = callback(check)
119 check = callback(check)
110
120
111 return check >= min_version
121 return check >= min_version
112 else:
122 else:
113 return True
123 return True
114
124
115 # Global dict where we can store information on what we have and what we don't
125 # Global dict where we can store information on what we have and what we don't
116 # have available at test run time
126 # have available at test run time
117 have = {'matplotlib': test_for('matplotlib'),
127 have = {'matplotlib': test_for('matplotlib'),
118 'pygments': test_for('pygments'),
128 'pygments': test_for('pygments'),
119 'sqlite3': test_for('sqlite3')}
129 'sqlite3': test_for('sqlite3')}
120
130
121 #-----------------------------------------------------------------------------
131 #-----------------------------------------------------------------------------
122 # Test suite definitions
132 # Test suite definitions
123 #-----------------------------------------------------------------------------
133 #-----------------------------------------------------------------------------
124
134
125 test_group_names = ['core',
135 test_group_names = ['core',
126 'extensions', 'lib', 'terminal', 'testing', 'utils',
136 'extensions', 'lib', 'terminal', 'testing', 'utils',
127 ]
137 ]
128
138
129 class TestSection(object):
139 class TestSection(object):
130 def __init__(self, name, includes):
140 def __init__(self, name, includes):
131 self.name = name
141 self.name = name
132 self.includes = includes
142 self.includes = includes
133 self.excludes = []
143 self.excludes = []
134 self.dependencies = []
144 self.dependencies = []
135 self.enabled = True
145 self.enabled = True
136
146
137 def exclude(self, module):
147 def exclude(self, module):
138 if not module.startswith('IPython'):
148 if not module.startswith('IPython'):
139 module = self.includes[0] + "." + module
149 module = self.includes[0] + "." + module
140 self.excludes.append(module.replace('.', os.sep))
150 self.excludes.append(module.replace('.', os.sep))
141
151
142 def requires(self, *packages):
152 def requires(self, *packages):
143 self.dependencies.extend(packages)
153 self.dependencies.extend(packages)
144
154
145 @property
155 @property
146 def will_run(self):
156 def will_run(self):
147 return self.enabled and all(have[p] for p in self.dependencies)
157 return self.enabled and all(have[p] for p in self.dependencies)
148
158
149 # Name -> (include, exclude, dependencies_met)
159 # Name -> (include, exclude, dependencies_met)
150 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
160 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
151
161
152
162
153 # Exclusions and dependencies
163 # Exclusions and dependencies
154 # ---------------------------
164 # ---------------------------
155
165
156 # core:
166 # core:
157 sec = test_sections['core']
167 sec = test_sections['core']
158 if not have['sqlite3']:
168 if not have['sqlite3']:
159 sec.exclude('tests.test_history')
169 sec.exclude('tests.test_history')
160 sec.exclude('history')
170 sec.exclude('history')
161 if not have['matplotlib']:
171 if not have['matplotlib']:
162 sec.exclude('pylabtools'),
172 sec.exclude('pylabtools'),
163 sec.exclude('tests.test_pylabtools')
173 sec.exclude('tests.test_pylabtools')
164
174
165 # lib:
175 # lib:
166 sec = test_sections['lib']
176 sec = test_sections['lib']
167 sec.exclude('kernel')
177 sec.exclude('kernel')
168 if not have['pygments']:
178 if not have['pygments']:
169 sec.exclude('tests.test_lexers')
179 sec.exclude('tests.test_lexers')
170 # We do this unconditionally, so that the test suite doesn't import
180 # We do this unconditionally, so that the test suite doesn't import
171 # gtk, changing the default encoding and masking some unicode bugs.
181 # gtk, changing the default encoding and masking some unicode bugs.
172 sec.exclude('inputhookgtk')
182 sec.exclude('inputhookgtk')
173 # We also do this unconditionally, because wx can interfere with Unix signals.
183 # We also do this unconditionally, because wx can interfere with Unix signals.
174 # There are currently no tests for it anyway.
184 # There are currently no tests for it anyway.
175 sec.exclude('inputhookwx')
185 sec.exclude('inputhookwx')
176 # Testing inputhook will need a lot of thought, to figure out
186 # Testing inputhook will need a lot of thought, to figure out
177 # how to have tests that don't lock up with the gui event
187 # how to have tests that don't lock up with the gui event
178 # loops in the picture
188 # loops in the picture
179 sec.exclude('inputhook')
189 sec.exclude('inputhook')
180
190
181 # testing:
191 # testing:
182 sec = test_sections['testing']
192 sec = test_sections['testing']
183 # These have to be skipped on win32 because they use echo, rm, cd, etc.
193 # These have to be skipped on win32 because they use echo, rm, cd, etc.
184 # See ticket https://github.com/ipython/ipython/issues/87
194 # See ticket https://github.com/ipython/ipython/issues/87
185 if sys.platform == 'win32':
195 if sys.platform == 'win32':
186 sec.exclude('plugin.test_exampleip')
196 sec.exclude('plugin.test_exampleip')
187 sec.exclude('plugin.dtexample')
197 sec.exclude('plugin.dtexample')
188
198
189 # don't run jupyter_console tests found via shim
199 # don't run jupyter_console tests found via shim
190 test_sections['terminal'].exclude('console')
200 test_sections['terminal'].exclude('console')
191
201
192 # extensions:
202 # extensions:
193 sec = test_sections['extensions']
203 sec = test_sections['extensions']
194 # This is deprecated in favour of rpy2
204 # This is deprecated in favour of rpy2
195 sec.exclude('rmagic')
205 sec.exclude('rmagic')
196 # autoreload does some strange stuff, so move it to its own test section
206 # autoreload does some strange stuff, so move it to its own test section
197 sec.exclude('autoreload')
207 sec.exclude('autoreload')
198 sec.exclude('tests.test_autoreload')
208 sec.exclude('tests.test_autoreload')
199 test_sections['autoreload'] = TestSection('autoreload',
209 test_sections['autoreload'] = TestSection('autoreload',
200 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
210 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
201 test_group_names.append('autoreload')
211 test_group_names.append('autoreload')
202
212
203
213
204 #-----------------------------------------------------------------------------
214 #-----------------------------------------------------------------------------
205 # Functions and classes
215 # Functions and classes
206 #-----------------------------------------------------------------------------
216 #-----------------------------------------------------------------------------
207
217
208 def check_exclusions_exist():
218 def check_exclusions_exist():
209 from IPython.paths import get_ipython_package_dir
219 from IPython.paths import get_ipython_package_dir
210 from warnings import warn
220 from warnings import warn
211 parent = os.path.dirname(get_ipython_package_dir())
221 parent = os.path.dirname(get_ipython_package_dir())
212 for sec in test_sections:
222 for sec in test_sections:
213 for pattern in sec.exclusions:
223 for pattern in sec.exclusions:
214 fullpath = pjoin(parent, pattern)
224 fullpath = pjoin(parent, pattern)
215 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
225 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
216 warn("Excluding nonexistent file: %r" % pattern)
226 warn("Excluding nonexistent file: %r" % pattern)
217
227
218
228
219 class ExclusionPlugin(Plugin):
229 class ExclusionPlugin(Plugin):
220 """A nose plugin to effect our exclusions of files and directories.
230 """A nose plugin to effect our exclusions of files and directories.
221 """
231 """
222 name = 'exclusions'
232 name = 'exclusions'
223 score = 3000 # Should come before any other plugins
233 score = 3000 # Should come before any other plugins
224
234
225 def __init__(self, exclude_patterns=None):
235 def __init__(self, exclude_patterns=None):
226 """
236 """
227 Parameters
237 Parameters
228 ----------
238 ----------
229
239
230 exclude_patterns : sequence of strings, optional
240 exclude_patterns : sequence of strings, optional
231 Filenames containing these patterns (as raw strings, not as regular
241 Filenames containing these patterns (as raw strings, not as regular
232 expressions) are excluded from the tests.
242 expressions) are excluded from the tests.
233 """
243 """
234 self.exclude_patterns = exclude_patterns or []
244 self.exclude_patterns = exclude_patterns or []
235 super(ExclusionPlugin, self).__init__()
245 super(ExclusionPlugin, self).__init__()
236
246
237 def options(self, parser, env=os.environ):
247 def options(self, parser, env=os.environ):
238 Plugin.options(self, parser, env)
248 Plugin.options(self, parser, env)
239
249
240 def configure(self, options, config):
250 def configure(self, options, config):
241 Plugin.configure(self, options, config)
251 Plugin.configure(self, options, config)
242 # Override nose trying to disable plugin.
252 # Override nose trying to disable plugin.
243 self.enabled = True
253 self.enabled = True
244
254
245 def wantFile(self, filename):
255 def wantFile(self, filename):
246 """Return whether the given filename should be scanned for tests.
256 """Return whether the given filename should be scanned for tests.
247 """
257 """
248 if any(pat in filename for pat in self.exclude_patterns):
258 if any(pat in filename for pat in self.exclude_patterns):
249 return False
259 return False
250 return None
260 return None
251
261
252 def wantDirectory(self, directory):
262 def wantDirectory(self, directory):
253 """Return whether the given directory should be scanned for tests.
263 """Return whether the given directory should be scanned for tests.
254 """
264 """
255 if any(pat in directory for pat in self.exclude_patterns):
265 if any(pat in directory for pat in self.exclude_patterns):
256 return False
266 return False
257 return None
267 return None
258
268
259
269
260 class StreamCapturer(Thread):
270 class StreamCapturer(Thread):
261 daemon = True # Don't hang if main thread crashes
271 daemon = True # Don't hang if main thread crashes
262 started = False
272 started = False
263 def __init__(self, echo=False):
273 def __init__(self, echo=False):
264 super(StreamCapturer, self).__init__()
274 super(StreamCapturer, self).__init__()
265 self.echo = echo
275 self.echo = echo
266 self.streams = []
276 self.streams = []
267 self.buffer = BytesIO()
277 self.buffer = BytesIO()
268 self.readfd, self.writefd = os.pipe()
278 self.readfd, self.writefd = os.pipe()
269 self.buffer_lock = Lock()
279 self.buffer_lock = Lock()
270 self.stop = Event()
280 self.stop = Event()
271
281
272 def run(self):
282 def run(self):
273 self.started = True
283 self.started = True
274
284
275 while not self.stop.is_set():
285 while not self.stop.is_set():
276 chunk = os.read(self.readfd, 1024)
286 chunk = os.read(self.readfd, 1024)
277
287
278 with self.buffer_lock:
288 with self.buffer_lock:
279 self.buffer.write(chunk)
289 self.buffer.write(chunk)
280 if self.echo:
290 if self.echo:
281 sys.stdout.write(bytes_to_str(chunk))
291 sys.stdout.write(bytes_to_str(chunk))
282
292
283 os.close(self.readfd)
293 os.close(self.readfd)
284 os.close(self.writefd)
294 os.close(self.writefd)
285
295
286 def reset_buffer(self):
296 def reset_buffer(self):
287 with self.buffer_lock:
297 with self.buffer_lock:
288 self.buffer.truncate(0)
298 self.buffer.truncate(0)
289 self.buffer.seek(0)
299 self.buffer.seek(0)
290
300
291 def get_buffer(self):
301 def get_buffer(self):
292 with self.buffer_lock:
302 with self.buffer_lock:
293 return self.buffer.getvalue()
303 return self.buffer.getvalue()
294
304
295 def ensure_started(self):
305 def ensure_started(self):
296 if not self.started:
306 if not self.started:
297 self.start()
307 self.start()
298
308
299 def halt(self):
309 def halt(self):
300 """Safely stop the thread."""
310 """Safely stop the thread."""
301 if not self.started:
311 if not self.started:
302 return
312 return
303
313
304 self.stop.set()
314 self.stop.set()
305 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
315 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
306 self.join()
316 self.join()
307
317
308 class SubprocessStreamCapturePlugin(Plugin):
318 class SubprocessStreamCapturePlugin(Plugin):
309 name='subprocstreams'
319 name='subprocstreams'
310 def __init__(self):
320 def __init__(self):
311 Plugin.__init__(self)
321 Plugin.__init__(self)
312 self.stream_capturer = StreamCapturer()
322 self.stream_capturer = StreamCapturer()
313 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
323 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
314 # This is ugly, but distant parts of the test machinery need to be able
324 # This is ugly, but distant parts of the test machinery need to be able
315 # to redirect streams, so we make the object globally accessible.
325 # to redirect streams, so we make the object globally accessible.
316 nose.iptest_stdstreams_fileno = self.get_write_fileno
326 nose.iptest_stdstreams_fileno = self.get_write_fileno
317
327
318 def get_write_fileno(self):
328 def get_write_fileno(self):
319 if self.destination == 'capture':
329 if self.destination == 'capture':
320 self.stream_capturer.ensure_started()
330 self.stream_capturer.ensure_started()
321 return self.stream_capturer.writefd
331 return self.stream_capturer.writefd
322 elif self.destination == 'discard':
332 elif self.destination == 'discard':
323 return os.open(os.devnull, os.O_WRONLY)
333 return os.open(os.devnull, os.O_WRONLY)
324 else:
334 else:
325 return sys.__stdout__.fileno()
335 return sys.__stdout__.fileno()
326
336
327 def configure(self, options, config):
337 def configure(self, options, config):
328 Plugin.configure(self, options, config)
338 Plugin.configure(self, options, config)
329 # Override nose trying to disable plugin.
339 # Override nose trying to disable plugin.
330 if self.destination == 'capture':
340 if self.destination == 'capture':
331 self.enabled = True
341 self.enabled = True
332
342
333 def startTest(self, test):
343 def startTest(self, test):
334 # Reset log capture
344 # Reset log capture
335 self.stream_capturer.reset_buffer()
345 self.stream_capturer.reset_buffer()
336
346
337 def formatFailure(self, test, err):
347 def formatFailure(self, test, err):
338 # Show output
348 # Show output
339 ec, ev, tb = err
349 ec, ev, tb = err
340 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
350 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
341 if captured.strip():
351 if captured.strip():
342 ev = safe_str(ev)
352 ev = safe_str(ev)
343 out = [ev, '>> begin captured subprocess output <<',
353 out = [ev, '>> begin captured subprocess output <<',
344 captured,
354 captured,
345 '>> end captured subprocess output <<']
355 '>> end captured subprocess output <<']
346 return ec, '\n'.join(out), tb
356 return ec, '\n'.join(out), tb
347
357
348 return err
358 return err
349
359
350 formatError = formatFailure
360 formatError = formatFailure
351
361
352 def finalize(self, result):
362 def finalize(self, result):
353 self.stream_capturer.halt()
363 self.stream_capturer.halt()
354
364
355
365
356 def run_iptest():
366 def run_iptest():
357 """Run the IPython test suite using nose.
367 """Run the IPython test suite using nose.
358
368
359 This function is called when this script is **not** called with the form
369 This function is called when this script is **not** called with the form
360 `iptest all`. It simply calls nose with appropriate command line flags
370 `iptest all`. It simply calls nose with appropriate command line flags
361 and accepts all of the standard nose arguments.
371 and accepts all of the standard nose arguments.
362 """
372 """
363 # Apply our monkeypatch to Xunit
373 # Apply our monkeypatch to Xunit
364 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
374 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
365 monkeypatch_xunit()
375 monkeypatch_xunit()
366
376
367 arg1 = sys.argv[1]
377 arg1 = sys.argv[1]
368 if arg1 in test_sections:
378 if arg1 in test_sections:
369 section = test_sections[arg1]
379 section = test_sections[arg1]
370 sys.argv[1:2] = section.includes
380 sys.argv[1:2] = section.includes
371 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
381 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
372 section = test_sections[arg1[8:]]
382 section = test_sections[arg1[8:]]
373 sys.argv[1:2] = section.includes
383 sys.argv[1:2] = section.includes
374 else:
384 else:
375 section = TestSection(arg1, includes=[arg1])
385 section = TestSection(arg1, includes=[arg1])
376
386
377
387
378 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
388 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
379 # We add --exe because of setuptools' imbecility (it
389 # We add --exe because of setuptools' imbecility (it
380 # blindly does chmod +x on ALL files). Nose does the
390 # blindly does chmod +x on ALL files). Nose does the
381 # right thing and it tries to avoid executables,
391 # right thing and it tries to avoid executables,
382 # setuptools unfortunately forces our hand here. This
392 # setuptools unfortunately forces our hand here. This
383 # has been discussed on the distutils list and the
393 # has been discussed on the distutils list and the
384 # setuptools devs refuse to fix this problem!
394 # setuptools devs refuse to fix this problem!
385 '--exe',
395 '--exe',
386 ]
396 ]
387 if '-a' not in argv and '-A' not in argv:
397 if '-a' not in argv and '-A' not in argv:
388 argv = argv + ['-a', '!crash']
398 argv = argv + ['-a', '!crash']
389
399
390 if nose.__version__ >= '0.11':
400 if nose.__version__ >= '0.11':
391 # I don't fully understand why we need this one, but depending on what
401 # I don't fully understand why we need this one, but depending on what
392 # directory the test suite is run from, if we don't give it, 0 tests
402 # directory the test suite is run from, if we don't give it, 0 tests
393 # get run. Specifically, if the test suite is run from the source dir
403 # get run. Specifically, if the test suite is run from the source dir
394 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
404 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
395 # even if the same call done in this directory works fine). It appears
405 # even if the same call done in this directory works fine). It appears
396 # that if the requested package is in the current dir, nose bails early
406 # that if the requested package is in the current dir, nose bails early
397 # by default. Since it's otherwise harmless, leave it in by default
407 # by default. Since it's otherwise harmless, leave it in by default
398 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
408 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
399 argv.append('--traverse-namespace')
409 argv.append('--traverse-namespace')
400
410
401 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
411 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
402 SubprocessStreamCapturePlugin() ]
412 SubprocessStreamCapturePlugin() ]
403
413
404 # we still have some vestigial doctests in core
414 # we still have some vestigial doctests in core
405 if (section.name.startswith(('core', 'IPython.core'))):
415 if (section.name.startswith(('core', 'IPython.core'))):
406 plugins.append(IPythonDoctest())
416 plugins.append(IPythonDoctest())
407 argv.extend([
417 argv.extend([
408 '--with-ipdoctest',
418 '--with-ipdoctest',
409 '--ipdoctest-tests',
419 '--ipdoctest-tests',
410 '--ipdoctest-extension=txt',
420 '--ipdoctest-extension=txt',
411 ])
421 ])
412
422
413
423
414 # Use working directory set by parent process (see iptestcontroller)
424 # Use working directory set by parent process (see iptestcontroller)
415 if 'IPTEST_WORKING_DIR' in os.environ:
425 if 'IPTEST_WORKING_DIR' in os.environ:
416 os.chdir(os.environ['IPTEST_WORKING_DIR'])
426 os.chdir(os.environ['IPTEST_WORKING_DIR'])
417
427
418 # We need a global ipython running in this process, but the special
428 # We need a global ipython running in this process, but the special
419 # in-process group spawns its own IPython kernels, so for *that* group we
429 # in-process group spawns its own IPython kernels, so for *that* group we
420 # must avoid also opening the global one (otherwise there's a conflict of
430 # must avoid also opening the global one (otherwise there's a conflict of
421 # singletons). Ultimately the solution to this problem is to refactor our
431 # singletons). Ultimately the solution to this problem is to refactor our
422 # assumptions about what needs to be a singleton and what doesn't (app
432 # assumptions about what needs to be a singleton and what doesn't (app
423 # objects should, individual shells shouldn't). But for now, this
433 # objects should, individual shells shouldn't). But for now, this
424 # workaround allows the test suite for the inprocess module to complete.
434 # workaround allows the test suite for the inprocess module to complete.
425 if 'kernel.inprocess' not in section.name:
435 if 'kernel.inprocess' not in section.name:
426 from IPython.testing import globalipapp
436 from IPython.testing import globalipapp
427 globalipapp.start_ipython()
437 globalipapp.start_ipython()
428
438
429 # Now nose can run
439 # Now nose can run
430 TestProgram(argv=argv, addplugins=plugins)
440 TestProgram(argv=argv, addplugins=plugins)
431
441
432 if __name__ == '__main__':
442 if __name__ == '__main__':
433 run_iptest()
443 run_iptest()
General Comments 0
You need to be logged in to leave comments. Login now