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