##// END OF EJS Templates
Backport PR #10039: Improve deprecation warnings...
Thomas Kluyver -
Show More
@@ -1,630 +1,630 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 from __future__ import print_function
28 28
29 29 import bdb
30 30 import functools
31 31 import inspect
32 32 import sys
33 33 import warnings
34 34
35 35 from IPython import get_ipython
36 36 from IPython.utils import PyColorize, ulinecache
37 37 from IPython.utils import coloransi, py3compat
38 38 from IPython.core.excolors import exception_colors
39 39 from IPython.testing.skipdoctest import skip_doctest
40 40
41 41
42 42 prompt = 'ipdb> '
43 43
44 44 #We have to check this directly from sys.argv, config struct not yet available
45 45 from pdb import Pdb as OldPdb
46 46
47 47 # Allow the set_trace code to operate outside of an ipython instance, even if
48 48 # it does so with some limitations. The rest of this support is implemented in
49 49 # the Tracer constructor.
50 50
51 51 def make_arrow(pad):
52 52 """generate the leading arrow in front of traceback or debugger"""
53 53 if pad >= 2:
54 54 return '-'*(pad-2) + '> '
55 55 elif pad == 1:
56 56 return '>'
57 57 return ''
58 58
59 59
60 60 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
61 61 """Exception hook which handles `BdbQuit` exceptions.
62 62
63 63 All other exceptions are processed using the `excepthook`
64 64 parameter.
65 65 """
66 66 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
67 DeprecationWarning)
67 DeprecationWarning, stacklevel=2)
68 68 if et==bdb.BdbQuit:
69 69 print('Exiting Debugger.')
70 70 elif excepthook is not None:
71 71 excepthook(et, ev, tb)
72 72 else:
73 73 # Backwards compatibility. Raise deprecation warning?
74 74 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
75 75
76 76
77 77 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
78 78 warnings.warn(
79 79 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
80 DeprecationWarning)
80 DeprecationWarning, stacklevel=2)
81 81 print('Exiting Debugger.')
82 82
83 83
84 84 class Tracer(object):
85 85 """
86 86 DEPRECATED
87 87
88 88 Class for local debugging, similar to pdb.set_trace.
89 89
90 90 Instances of this class, when called, behave like pdb.set_trace, but
91 91 providing IPython's enhanced capabilities.
92 92
93 93 This is implemented as a class which must be initialized in your own code
94 94 and not as a standalone function because we need to detect at runtime
95 95 whether IPython is already active or not. That detection is done in the
96 96 constructor, ensuring that this code plays nicely with a running IPython,
97 97 while functioning acceptably (though with limitations) if outside of it.
98 98 """
99 99
100 100 @skip_doctest
101 101 def __init__(self, colors=None):
102 102 """
103 103 DEPRECATED
104 104
105 105 Create a local debugger instance.
106 106
107 107 Parameters
108 108 ----------
109 109
110 110 colors : str, optional
111 111 The name of the color scheme to use, it must be one of IPython's
112 112 valid color schemes. If not given, the function will default to
113 113 the current IPython scheme when running inside IPython, and to
114 114 'NoColor' otherwise.
115 115
116 116 Examples
117 117 --------
118 118 ::
119 119
120 120 from IPython.core.debugger import Tracer; debug_here = Tracer()
121 121
122 122 Later in your code::
123 123
124 124 debug_here() # -> will open up the debugger at that point.
125 125
126 126 Once the debugger activates, you can use all of its regular commands to
127 127 step through code, set breakpoints, etc. See the pdb documentation
128 128 from the Python standard library for usage details.
129 129 """
130 130 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
131 131 "`IPython.core.debugger.Pdb.set_trace()`",
132 DeprecationWarning)
132 DeprecationWarning, stacklevel=2)
133 133
134 134 ip = get_ipython()
135 135 if ip is None:
136 136 # Outside of ipython, we set our own exception hook manually
137 137 sys.excepthook = functools.partial(BdbQuit_excepthook,
138 138 excepthook=sys.excepthook)
139 139 def_colors = 'NoColor'
140 140 else:
141 141 # In ipython, we use its custom exception handler mechanism
142 142 def_colors = ip.colors
143 143 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
144 144
145 145 if colors is None:
146 146 colors = def_colors
147 147
148 148 # The stdlib debugger internally uses a modified repr from the `repr`
149 149 # module, that limits the length of printed strings to a hardcoded
150 150 # limit of 30 characters. That much trimming is too aggressive, let's
151 151 # at least raise that limit to 80 chars, which should be enough for
152 152 # most interactive uses.
153 153 try:
154 154 try:
155 155 from reprlib import aRepr # Py 3
156 156 except ImportError:
157 157 from repr import aRepr # Py 2
158 158 aRepr.maxstring = 80
159 159 except:
160 160 # This is only a user-facing convenience, so any error we encounter
161 161 # here can be warned about but can be otherwise ignored. These
162 162 # printouts will tell us about problems if this API changes
163 163 import traceback
164 164 traceback.print_exc()
165 165
166 166 self.debugger = Pdb(colors)
167 167
168 168 def __call__(self):
169 169 """Starts an interactive debugger at the point where called.
170 170
171 171 This is similar to the pdb.set_trace() function from the std lib, but
172 172 using IPython's enhanced debugger."""
173 173
174 174 self.debugger.set_trace(sys._getframe().f_back)
175 175
176 176
177 177 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
178 178 """Make new_fn have old_fn's doc string. This is particularly useful
179 179 for the ``do_...`` commands that hook into the help system.
180 180 Adapted from from a comp.lang.python posting
181 181 by Duncan Booth."""
182 182 def wrapper(*args, **kw):
183 183 return new_fn(*args, **kw)
184 184 if old_fn.__doc__:
185 185 wrapper.__doc__ = old_fn.__doc__ + additional_text
186 186 return wrapper
187 187
188 188
189 189 def _file_lines(fname):
190 190 """Return the contents of a named file as a list of lines.
191 191
192 192 This function never raises an IOError exception: if the file can't be
193 193 read, it simply returns an empty list."""
194 194
195 195 try:
196 196 outfile = open(fname)
197 197 except IOError:
198 198 return []
199 199 else:
200 200 out = outfile.readlines()
201 201 outfile.close()
202 202 return out
203 203
204 204
205 205 class Pdb(OldPdb, object):
206 206 """Modified Pdb class, does not load readline.
207 207
208 208 for a standalone version that uses prompt_toolkit, see
209 209 `IPython.terminal.debugger.TerminalPdb` and
210 210 `IPython.terminal.debugger.set_trace()`
211 211 """
212 212
213 213 def __init__(self, color_scheme=None, completekey=None,
214 214 stdin=None, stdout=None, context=5):
215 215
216 216 # Parent constructor:
217 217 try:
218 218 self.context = int(context)
219 219 if self.context <= 0:
220 220 raise ValueError("Context must be a positive integer")
221 221 except (TypeError, ValueError):
222 222 raise ValueError("Context must be a positive integer")
223 223
224 224 OldPdb.__init__(self, completekey, stdin, stdout)
225 225
226 226 # IPython changes...
227 227 self.shell = get_ipython()
228 228
229 229 if self.shell is None:
230 230 save_main = sys.modules['__main__']
231 231 # No IPython instance running, we must create one
232 232 from IPython.terminal.interactiveshell import \
233 233 TerminalInteractiveShell
234 234 self.shell = TerminalInteractiveShell.instance()
235 235 # needed by any code which calls __import__("__main__") after
236 236 # the debugger was entered. See also #9941.
237 237 sys.modules['__main__'] = save_main
238 238
239 239 if color_scheme is not None:
240 240 warnings.warn(
241 241 "The `color_scheme` argument is deprecated since version 5.1",
242 242 DeprecationWarning)
243 243 else:
244 244 color_scheme = self.shell.colors
245 245
246 246 self.aliases = {}
247 247
248 248 # Create color table: we copy the default one from the traceback
249 249 # module and add a few attributes needed for debugging
250 250 self.color_scheme_table = exception_colors()
251 251
252 252 # shorthands
253 253 C = coloransi.TermColors
254 254 cst = self.color_scheme_table
255 255
256 256 cst['NoColor'].colors.prompt = C.NoColor
257 257 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
258 258 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
259 259
260 260 cst['Linux'].colors.prompt = C.Green
261 261 cst['Linux'].colors.breakpoint_enabled = C.LightRed
262 262 cst['Linux'].colors.breakpoint_disabled = C.Red
263 263
264 264 cst['LightBG'].colors.prompt = C.Blue
265 265 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
266 266 cst['LightBG'].colors.breakpoint_disabled = C.Red
267 267
268 268 cst['Neutral'].colors.prompt = C.Blue
269 269 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
270 270 cst['Neutral'].colors.breakpoint_disabled = C.Red
271 271
272 272 self.set_colors(color_scheme)
273 273
274 274 # Add a python parser so we can syntax highlight source while
275 275 # debugging.
276 276 self.parser = PyColorize.Parser()
277 277
278 278 # Set the prompt - the default prompt is '(Pdb)'
279 279 self.prompt = prompt
280 280
281 281 def set_colors(self, scheme):
282 282 """Shorthand access to the color table scheme selector method."""
283 283 self.color_scheme_table.set_active_scheme(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 scheme = self.color_scheme_table.active_scheme_name
449 449 new_line, err = self.parser.format2(line, 'str', scheme)
450 450 if not err: 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 if sys.version_info > (3, ):
607 607 def do_where(self, arg):
608 608 """w(here)
609 609 Print a stack trace, with the most recent frame at the bottom.
610 610 An arrow indicates the "current frame", which determines the
611 611 context of most commands. 'bt' is an alias for this command.
612 612
613 613 Take a number as argument as an (optional) number of context line to
614 614 print"""
615 615 if arg:
616 616 context = int(arg)
617 617 self.print_stack_trace(context)
618 618 else:
619 619 self.print_stack_trace()
620 620
621 621 do_w = do_where
622 622
623 623
624 624 def set_trace(frame=None):
625 625 """
626 626 Start debugging from `frame`.
627 627
628 628 If frame is not specified, debugging starts from caller's frame.
629 629 """
630 630 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,380 +1,384 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
40 40 from decorator import decorator
41 41
42 42 # Expose the unittest-driven decorators
43 43 from .ipunittest import ipdoctest, ipdocstring
44 44
45 45 # Grab the numpy-specific decorators which we keep in a file that we
46 46 # occasionally update from upstream: decorators.py is a copy of
47 47 # numpy.testing.decorators, we expose all of it here.
48 48 from IPython.external.decorators import *
49 49
50 50 # For onlyif_cmd_exists decorator
51 51 from IPython.utils.py3compat import string_types, which, PY2, PY3, PYPY
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Classes and functions
55 55 #-----------------------------------------------------------------------------
56 56
57 57 # Simple example of the basic idea
58 58 def as_unittest(func):
59 59 """Decorator to make a simple function into a normal test via unittest."""
60 60 class Tester(unittest.TestCase):
61 61 def test(self):
62 62 func()
63 63
64 64 Tester.__name__ = func.__name__
65 65
66 66 return Tester
67 67
68 68 # Utility functions
69 69
70 70 def apply_wrapper(wrapper,func):
71 71 """Apply a wrapper to a function for decoration.
72 72
73 73 This mixes Michele Simionato's decorator tool with nose's make_decorator,
74 74 to apply a wrapper in a decorator so that all nose attributes, as well as
75 75 function signature and other properties, survive the decoration cleanly.
76 76 This will ensure that wrapped functions can still be well introspected via
77 77 IPython, for example.
78 78 """
79 warnings.warn("The function `apply_wrapper` is deprecated and might be removed in IPython 5.0", DeprecationWarning)
80
79 warnings.warn("The function `apply_wrapper` is deprecated since IPython 4.0",
80 DeprecationWarning, stacklevel=2)
81 81 import nose.tools
82 82
83 83 return decorator(wrapper,nose.tools.make_decorator(func)(wrapper))
84 84
85 85
86 86 def make_label_dec(label,ds=None):
87 87 """Factory function to create a decorator that applies one or more labels.
88 88
89 89 Parameters
90 90 ----------
91 91 label : string or sequence
92 92 One or more labels that will be applied by the decorator to the functions
93 93 it decorates. Labels are attributes of the decorated function with their
94 94 value set to True.
95 95
96 96 ds : string
97 97 An optional docstring for the resulting decorator. If not given, a
98 98 default docstring is auto-generated.
99 99
100 100 Returns
101 101 -------
102 102 A decorator.
103 103
104 104 Examples
105 105 --------
106 106
107 107 A simple labeling decorator:
108 108
109 109 >>> slow = make_label_dec('slow')
110 110 >>> slow.__doc__
111 111 "Labels a test as 'slow'."
112 112
113 113 And one that uses multiple labels and a custom docstring:
114 114
115 115 >>> rare = make_label_dec(['slow','hard'],
116 116 ... "Mix labels 'slow' and 'hard' for rare tests.")
117 117 >>> rare.__doc__
118 118 "Mix labels 'slow' and 'hard' for rare tests."
119 119
120 120 Now, let's test using this one:
121 121 >>> @rare
122 122 ... def f(): pass
123 123 ...
124 124 >>>
125 125 >>> f.slow
126 126 True
127 127 >>> f.hard
128 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 133 if isinstance(label, string_types):
133 134 labels = [label]
134 135 else:
135 136 labels = label
136 137
137 138 # Validate that the given label(s) are OK for use in setattr() by doing a
138 139 # dry run on a dummy function.
139 140 tmp = lambda : None
140 141 for label in labels:
141 142 setattr(tmp,label,True)
142 143
143 144 # This is the actual decorator we'll return
144 145 def decor(f):
145 146 for label in labels:
146 147 setattr(f,label,True)
147 148 return f
148 149
149 150 # Apply the user's docstring, or autogenerate a basic one
150 151 if ds is None:
151 152 ds = "Labels a test as %r." % label
152 153 decor.__doc__ = ds
153 154
154 155 return decor
155 156
156 157
157 158 # Inspired by numpy's skipif, but uses the full apply_wrapper utility to
158 159 # preserve function metadata better and allows the skip condition to be a
159 160 # callable.
160 161 def skipif(skip_condition, msg=None):
161 162 ''' Make function raise SkipTest exception if skip_condition is true
162 163
163 164 Parameters
164 165 ----------
165 166
166 167 skip_condition : bool or callable
167 168 Flag to determine whether to skip test. If the condition is a
168 169 callable, it is used at runtime to dynamically make the decision. This
169 170 is useful for tests that may require costly imports, to delay the cost
170 171 until the test suite is actually executed.
171 172 msg : string
172 173 Message to give on raising a SkipTest exception.
173 174
174 175 Returns
175 176 -------
176 177 decorator : function
177 178 Decorator, which, when applied to a function, causes SkipTest
178 179 to be raised when the skip_condition was True, and the function
179 180 to be called normally otherwise.
180 181
181 182 Notes
182 183 -----
183 184 You will see from the code that we had to further decorate the
184 185 decorator with the nose.tools.make_decorator function in order to
185 186 transmit function name, and various other metadata.
186 187 '''
187 188
188 189 def skip_decorator(f):
189 190 # Local import to avoid a hard nose dependency and only incur the
190 191 # import time overhead at actual test-time.
191 192 import nose
192 193
193 194 # Allow for both boolean or callable skip conditions.
194 195 if callable(skip_condition):
195 196 skip_val = skip_condition
196 197 else:
197 198 skip_val = lambda : skip_condition
198 199
199 200 def get_msg(func,msg=None):
200 201 """Skip message with information about function being skipped."""
201 202 if msg is None: out = 'Test skipped due to test condition.'
202 203 else: out = msg
203 204 return "Skipping test: %s. %s" % (func.__name__,out)
204 205
205 206 # We need to define *two* skippers because Python doesn't allow both
206 207 # return with value and yield inside the same function.
207 208 def skipper_func(*args, **kwargs):
208 209 """Skipper for normal test functions."""
209 210 if skip_val():
210 211 raise nose.SkipTest(get_msg(f,msg))
211 212 else:
212 213 return f(*args, **kwargs)
213 214
214 215 def skipper_gen(*args, **kwargs):
215 216 """Skipper for test generators."""
216 217 if skip_val():
217 218 raise nose.SkipTest(get_msg(f,msg))
218 219 else:
219 220 for x in f(*args, **kwargs):
220 221 yield x
221 222
222 223 # Choose the right skipper to use when building the actual generator.
223 224 if nose.util.isgenerator(f):
224 225 skipper = skipper_gen
225 226 else:
226 227 skipper = skipper_func
227 228
228 229 return nose.tools.make_decorator(f)(skipper)
229 230
230 231 return skip_decorator
231 232
232 233 # A version with the condition set to true, common case just to attach a message
233 234 # to a skip decorator
234 235 def skip(msg=None):
235 236 """Decorator factory - mark a test function for skipping from test suite.
236 237
237 238 Parameters
238 239 ----------
239 240 msg : string
240 241 Optional message to be added.
241 242
242 243 Returns
243 244 -------
244 245 decorator : function
245 246 Decorator, which, when applied to a function, causes SkipTest
246 247 to be raised, with the optional message added.
247 248 """
248 249
249 250 return skipif(True,msg)
250 251
251 252
252 253 def onlyif(condition, msg):
253 254 """The reverse from skipif, see skipif for details."""
254 255
255 256 if callable(condition):
256 257 skip_condition = lambda : not condition()
257 258 else:
258 259 skip_condition = lambda : not condition
259 260
260 261 return skipif(skip_condition, msg)
261 262
262 263 #-----------------------------------------------------------------------------
263 264 # Utility functions for decorators
264 265 def module_not_available(module):
265 266 """Can module be imported? Returns true if module does NOT import.
266 267
267 268 This is used to make a decorator to skip tests that require module to be
268 269 available, but delay the 'import numpy' to test execution time.
269 270 """
270 271 try:
271 272 mod = __import__(module)
272 273 mod_not_avail = False
273 274 except ImportError:
274 275 mod_not_avail = True
275 276
276 277 return mod_not_avail
277 278
278 279
279 280 def decorated_dummy(dec, name):
280 281 """Return a dummy function decorated with dec, with the given name.
281 282
282 283 Examples
283 284 --------
284 285 import IPython.testing.decorators as dec
285 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 290 dummy = lambda: None
289 291 dummy.__name__ = name
290 292 return dec(dummy)
291 293
292 294 #-----------------------------------------------------------------------------
293 295 # Decorators for public use
294 296
295 297 # Decorators to skip certain tests on specific platforms.
296 298 skip_win32 = skipif(sys.platform == 'win32',
297 299 "This test does not run under Windows")
298 300 skip_linux = skipif(sys.platform.startswith('linux'),
299 301 "This test does not run under Linux")
300 302 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
301 303
302 304
303 305 # Decorators to skip tests if not on specific platforms.
304 306 skip_if_not_win32 = skipif(sys.platform != 'win32',
305 307 "This test only runs under Windows")
306 308 skip_if_not_linux = skipif(not sys.platform.startswith('linux'),
307 309 "This test only runs under Linux")
308 310 skip_if_not_osx = skipif(sys.platform != 'darwin',
309 311 "This test only runs under OSX")
310 312
311 313
312 314 _x11_skip_cond = (sys.platform not in ('darwin', 'win32') and
313 315 os.environ.get('DISPLAY', '') == '')
314 316 _x11_skip_msg = "Skipped under *nix when X11/XOrg not available"
315 317
316 318 skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg)
317 319
318 320 # not a decorator itself, returns a dummy function to be used as setup
319 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 324 return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None
322 325
323 326 # Other skip decorators
324 327
325 328 # generic skip without module
326 329 skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod)
327 330
328 331 skipif_not_numpy = skip_without('numpy')
329 332
330 333 skipif_not_matplotlib = skip_without('matplotlib')
331 334
332 335 skipif_not_sympy = skip_without('sympy')
333 336
334 337 skip_known_failure = knownfailureif(True,'This test is known to fail')
335 338
336 339 known_failure_py3 = knownfailureif(sys.version_info[0] >= 3,
337 340 'This test is known to fail on Python 3.')
338 341
339 342 cpython2_only = skipif(PY3 or PYPY, "This test only runs on CPython 2.")
340 343 py2_only = skipif(PY3, "This test only runs on Python 2.")
341 344 py3_only = skipif(PY2, "This test only runs on Python 3.")
342 345
343 346 # A null 'decorator', useful to make more readable code that needs to pick
344 347 # between different decorators based on OS or other conditions
345 348 null_deco = lambda f: f
346 349
347 350 # Some tests only run where we can use unicode paths. Note that we can't just
348 351 # check os.path.supports_unicode_filenames, which is always False on Linux.
349 352 try:
350 353 f = tempfile.NamedTemporaryFile(prefix=u"tmp€")
351 354 except UnicodeEncodeError:
352 355 unicode_paths = False
353 356 else:
354 357 unicode_paths = True
355 358 f.close()
356 359
357 360 onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable "
358 361 "where we can use unicode in filenames."))
359 362
360 363
361 364 def onlyif_cmds_exist(*commands):
362 365 """
363 366 Decorator to skip test when at least one of `commands` is not found.
364 367 """
365 368 for cmd in commands:
366 369 if not which(cmd):
367 370 return skip("This test runs only if command '{0}' "
368 371 "is installed".format(cmd))
369 372 return null_deco
370 373
371 374 def onlyif_any_cmd_exists(*commands):
372 375 """
373 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 380 for cmd in commands:
377 381 if which(cmd):
378 382 return null_deco
379 383 return skip("This test runs only if one of the commands {0} "
380 384 "is installed".format(commands))
@@ -1,433 +1,443 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 from __future__ import print_function
21 21
22 22 import glob
23 23 from io import BytesIO
24 24 import os
25 25 import os.path as path
26 26 import sys
27 27 from threading import Thread, Lock, Event
28 28 import warnings
29 29
30 30 import nose.plugins.builtin
31 31 from nose.plugins.xunit import Xunit
32 32 from nose import SkipTest
33 33 from nose.core import TestProgram
34 34 from nose.plugins import Plugin
35 35 from nose.util import safe_str
36 36
37 37 from IPython import version_info
38 38 from IPython.utils.py3compat import bytes_to_str
39 39 from IPython.utils.importstring import import_item
40 40 from IPython.testing.plugin.ipdoctest import IPythonDoctest
41 41 from IPython.external.decorators import KnownFailure, knownfailureif
42 42
43 43 pjoin = path.join
44 44
45 45
46 46 # Enable printing all warnings raise by IPython's modules
47 47 warnings.filterwarnings('ignore', message='.*Matplotlib is building the font cache.*', category=UserWarning, module='.*')
48 48 if sys.version_info > (3,0):
49 49 warnings.filterwarnings('error', message='.*', category=ResourceWarning, module='.*')
50 50 warnings.filterwarnings('error', message=".*{'config': True}.*", category=DeprecationWarning, module='IPy.*')
51 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 63 if version_info < (6,):
54 64 # nose.tools renames all things from `camelCase` to `snake_case` which raise an
55 65 # warning with the runner they also import from standard import library. (as of Dec 2015)
56 66 # Ignore, let's revisit that in a couple of years for IPython 6.
57 67 warnings.filterwarnings('ignore', message='.*Please use assertEqual instead', category=Warning, module='IPython.*')
58 68
59 69
60 70 # ------------------------------------------------------------------------------
61 71 # Monkeypatch Xunit to count known failures as skipped.
62 72 # ------------------------------------------------------------------------------
63 73 def monkeypatch_xunit():
64 74 try:
65 75 knownfailureif(True)(lambda: None)()
66 76 except Exception as e:
67 77 KnownFailureTest = type(e)
68 78
69 79 def addError(self, test, err, capt=None):
70 80 if issubclass(err[0], KnownFailureTest):
71 81 err = (SkipTest,) + err[1:]
72 82 return self.orig_addError(test, err, capt)
73 83
74 84 Xunit.orig_addError = Xunit.addError
75 85 Xunit.addError = addError
76 86
77 87 #-----------------------------------------------------------------------------
78 88 # Check which dependencies are installed and greater than minimum version.
79 89 #-----------------------------------------------------------------------------
80 90 def extract_version(mod):
81 91 return mod.__version__
82 92
83 93 def test_for(item, min_version=None, callback=extract_version):
84 94 """Test to see if item is importable, and optionally check against a minimum
85 95 version.
86 96
87 97 If min_version is given, the default behavior is to check against the
88 98 `__version__` attribute of the item, but specifying `callback` allows you to
89 99 extract the value you are interested in. e.g::
90 100
91 101 In [1]: import sys
92 102
93 103 In [2]: from IPython.testing.iptest import test_for
94 104
95 105 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
96 106 Out[3]: True
97 107
98 108 """
99 109 try:
100 110 check = import_item(item)
101 111 except (ImportError, RuntimeError):
102 112 # GTK reports Runtime error if it can't be initialized even if it's
103 113 # importable.
104 114 return False
105 115 else:
106 116 if min_version:
107 117 if callback:
108 118 # extra processing step to get version to compare
109 119 check = callback(check)
110 120
111 121 return check >= min_version
112 122 else:
113 123 return True
114 124
115 125 # Global dict where we can store information on what we have and what we don't
116 126 # have available at test run time
117 127 have = {'matplotlib': test_for('matplotlib'),
118 128 'pygments': test_for('pygments'),
119 129 'sqlite3': test_for('sqlite3')}
120 130
121 131 #-----------------------------------------------------------------------------
122 132 # Test suite definitions
123 133 #-----------------------------------------------------------------------------
124 134
125 135 test_group_names = ['core',
126 136 'extensions', 'lib', 'terminal', 'testing', 'utils',
127 137 ]
128 138
129 139 class TestSection(object):
130 140 def __init__(self, name, includes):
131 141 self.name = name
132 142 self.includes = includes
133 143 self.excludes = []
134 144 self.dependencies = []
135 145 self.enabled = True
136 146
137 147 def exclude(self, module):
138 148 if not module.startswith('IPython'):
139 149 module = self.includes[0] + "." + module
140 150 self.excludes.append(module.replace('.', os.sep))
141 151
142 152 def requires(self, *packages):
143 153 self.dependencies.extend(packages)
144 154
145 155 @property
146 156 def will_run(self):
147 157 return self.enabled and all(have[p] for p in self.dependencies)
148 158
149 159 # Name -> (include, exclude, dependencies_met)
150 160 test_sections = {n:TestSection(n, ['IPython.%s' % n]) for n in test_group_names}
151 161
152 162
153 163 # Exclusions and dependencies
154 164 # ---------------------------
155 165
156 166 # core:
157 167 sec = test_sections['core']
158 168 if not have['sqlite3']:
159 169 sec.exclude('tests.test_history')
160 170 sec.exclude('history')
161 171 if not have['matplotlib']:
162 172 sec.exclude('pylabtools'),
163 173 sec.exclude('tests.test_pylabtools')
164 174
165 175 # lib:
166 176 sec = test_sections['lib']
167 177 sec.exclude('kernel')
168 178 if not have['pygments']:
169 179 sec.exclude('tests.test_lexers')
170 180 # We do this unconditionally, so that the test suite doesn't import
171 181 # gtk, changing the default encoding and masking some unicode bugs.
172 182 sec.exclude('inputhookgtk')
173 183 # We also do this unconditionally, because wx can interfere with Unix signals.
174 184 # There are currently no tests for it anyway.
175 185 sec.exclude('inputhookwx')
176 186 # Testing inputhook will need a lot of thought, to figure out
177 187 # how to have tests that don't lock up with the gui event
178 188 # loops in the picture
179 189 sec.exclude('inputhook')
180 190
181 191 # testing:
182 192 sec = test_sections['testing']
183 193 # These have to be skipped on win32 because they use echo, rm, cd, etc.
184 194 # See ticket https://github.com/ipython/ipython/issues/87
185 195 if sys.platform == 'win32':
186 196 sec.exclude('plugin.test_exampleip')
187 197 sec.exclude('plugin.dtexample')
188 198
189 199 # don't run jupyter_console tests found via shim
190 200 test_sections['terminal'].exclude('console')
191 201
192 202 # extensions:
193 203 sec = test_sections['extensions']
194 204 # This is deprecated in favour of rpy2
195 205 sec.exclude('rmagic')
196 206 # autoreload does some strange stuff, so move it to its own test section
197 207 sec.exclude('autoreload')
198 208 sec.exclude('tests.test_autoreload')
199 209 test_sections['autoreload'] = TestSection('autoreload',
200 210 ['IPython.extensions.autoreload', 'IPython.extensions.tests.test_autoreload'])
201 211 test_group_names.append('autoreload')
202 212
203 213
204 214 #-----------------------------------------------------------------------------
205 215 # Functions and classes
206 216 #-----------------------------------------------------------------------------
207 217
208 218 def check_exclusions_exist():
209 219 from IPython.paths import get_ipython_package_dir
210 220 from warnings import warn
211 221 parent = os.path.dirname(get_ipython_package_dir())
212 222 for sec in test_sections:
213 223 for pattern in sec.exclusions:
214 224 fullpath = pjoin(parent, pattern)
215 225 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
216 226 warn("Excluding nonexistent file: %r" % pattern)
217 227
218 228
219 229 class ExclusionPlugin(Plugin):
220 230 """A nose plugin to effect our exclusions of files and directories.
221 231 """
222 232 name = 'exclusions'
223 233 score = 3000 # Should come before any other plugins
224 234
225 235 def __init__(self, exclude_patterns=None):
226 236 """
227 237 Parameters
228 238 ----------
229 239
230 240 exclude_patterns : sequence of strings, optional
231 241 Filenames containing these patterns (as raw strings, not as regular
232 242 expressions) are excluded from the tests.
233 243 """
234 244 self.exclude_patterns = exclude_patterns or []
235 245 super(ExclusionPlugin, self).__init__()
236 246
237 247 def options(self, parser, env=os.environ):
238 248 Plugin.options(self, parser, env)
239 249
240 250 def configure(self, options, config):
241 251 Plugin.configure(self, options, config)
242 252 # Override nose trying to disable plugin.
243 253 self.enabled = True
244 254
245 255 def wantFile(self, filename):
246 256 """Return whether the given filename should be scanned for tests.
247 257 """
248 258 if any(pat in filename for pat in self.exclude_patterns):
249 259 return False
250 260 return None
251 261
252 262 def wantDirectory(self, directory):
253 263 """Return whether the given directory should be scanned for tests.
254 264 """
255 265 if any(pat in directory for pat in self.exclude_patterns):
256 266 return False
257 267 return None
258 268
259 269
260 270 class StreamCapturer(Thread):
261 271 daemon = True # Don't hang if main thread crashes
262 272 started = False
263 273 def __init__(self, echo=False):
264 274 super(StreamCapturer, self).__init__()
265 275 self.echo = echo
266 276 self.streams = []
267 277 self.buffer = BytesIO()
268 278 self.readfd, self.writefd = os.pipe()
269 279 self.buffer_lock = Lock()
270 280 self.stop = Event()
271 281
272 282 def run(self):
273 283 self.started = True
274 284
275 285 while not self.stop.is_set():
276 286 chunk = os.read(self.readfd, 1024)
277 287
278 288 with self.buffer_lock:
279 289 self.buffer.write(chunk)
280 290 if self.echo:
281 291 sys.stdout.write(bytes_to_str(chunk))
282 292
283 293 os.close(self.readfd)
284 294 os.close(self.writefd)
285 295
286 296 def reset_buffer(self):
287 297 with self.buffer_lock:
288 298 self.buffer.truncate(0)
289 299 self.buffer.seek(0)
290 300
291 301 def get_buffer(self):
292 302 with self.buffer_lock:
293 303 return self.buffer.getvalue()
294 304
295 305 def ensure_started(self):
296 306 if not self.started:
297 307 self.start()
298 308
299 309 def halt(self):
300 310 """Safely stop the thread."""
301 311 if not self.started:
302 312 return
303 313
304 314 self.stop.set()
305 315 os.write(self.writefd, b'\0') # Ensure we're not locked in a read()
306 316 self.join()
307 317
308 318 class SubprocessStreamCapturePlugin(Plugin):
309 319 name='subprocstreams'
310 320 def __init__(self):
311 321 Plugin.__init__(self)
312 322 self.stream_capturer = StreamCapturer()
313 323 self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture')
314 324 # This is ugly, but distant parts of the test machinery need to be able
315 325 # to redirect streams, so we make the object globally accessible.
316 326 nose.iptest_stdstreams_fileno = self.get_write_fileno
317 327
318 328 def get_write_fileno(self):
319 329 if self.destination == 'capture':
320 330 self.stream_capturer.ensure_started()
321 331 return self.stream_capturer.writefd
322 332 elif self.destination == 'discard':
323 333 return os.open(os.devnull, os.O_WRONLY)
324 334 else:
325 335 return sys.__stdout__.fileno()
326 336
327 337 def configure(self, options, config):
328 338 Plugin.configure(self, options, config)
329 339 # Override nose trying to disable plugin.
330 340 if self.destination == 'capture':
331 341 self.enabled = True
332 342
333 343 def startTest(self, test):
334 344 # Reset log capture
335 345 self.stream_capturer.reset_buffer()
336 346
337 347 def formatFailure(self, test, err):
338 348 # Show output
339 349 ec, ev, tb = err
340 350 captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace')
341 351 if captured.strip():
342 352 ev = safe_str(ev)
343 353 out = [ev, '>> begin captured subprocess output <<',
344 354 captured,
345 355 '>> end captured subprocess output <<']
346 356 return ec, '\n'.join(out), tb
347 357
348 358 return err
349 359
350 360 formatError = formatFailure
351 361
352 362 def finalize(self, result):
353 363 self.stream_capturer.halt()
354 364
355 365
356 366 def run_iptest():
357 367 """Run the IPython test suite using nose.
358 368
359 369 This function is called when this script is **not** called with the form
360 370 `iptest all`. It simply calls nose with appropriate command line flags
361 371 and accepts all of the standard nose arguments.
362 372 """
363 373 # Apply our monkeypatch to Xunit
364 374 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
365 375 monkeypatch_xunit()
366 376
367 377 arg1 = sys.argv[1]
368 378 if arg1 in test_sections:
369 379 section = test_sections[arg1]
370 380 sys.argv[1:2] = section.includes
371 381 elif arg1.startswith('IPython.') and arg1[8:] in test_sections:
372 382 section = test_sections[arg1[8:]]
373 383 sys.argv[1:2] = section.includes
374 384 else:
375 385 section = TestSection(arg1, includes=[arg1])
376 386
377 387
378 388 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
379 389 # We add --exe because of setuptools' imbecility (it
380 390 # blindly does chmod +x on ALL files). Nose does the
381 391 # right thing and it tries to avoid executables,
382 392 # setuptools unfortunately forces our hand here. This
383 393 # has been discussed on the distutils list and the
384 394 # setuptools devs refuse to fix this problem!
385 395 '--exe',
386 396 ]
387 397 if '-a' not in argv and '-A' not in argv:
388 398 argv = argv + ['-a', '!crash']
389 399
390 400 if nose.__version__ >= '0.11':
391 401 # I don't fully understand why we need this one, but depending on what
392 402 # directory the test suite is run from, if we don't give it, 0 tests
393 403 # get run. Specifically, if the test suite is run from the source dir
394 404 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
395 405 # even if the same call done in this directory works fine). It appears
396 406 # that if the requested package is in the current dir, nose bails early
397 407 # by default. Since it's otherwise harmless, leave it in by default
398 408 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
399 409 argv.append('--traverse-namespace')
400 410
401 411 plugins = [ ExclusionPlugin(section.excludes), KnownFailure(),
402 412 SubprocessStreamCapturePlugin() ]
403 413
404 414 # we still have some vestigial doctests in core
405 415 if (section.name.startswith(('core', 'IPython.core'))):
406 416 plugins.append(IPythonDoctest())
407 417 argv.extend([
408 418 '--with-ipdoctest',
409 419 '--ipdoctest-tests',
410 420 '--ipdoctest-extension=txt',
411 421 ])
412 422
413 423
414 424 # Use working directory set by parent process (see iptestcontroller)
415 425 if 'IPTEST_WORKING_DIR' in os.environ:
416 426 os.chdir(os.environ['IPTEST_WORKING_DIR'])
417 427
418 428 # We need a global ipython running in this process, but the special
419 429 # in-process group spawns its own IPython kernels, so for *that* group we
420 430 # must avoid also opening the global one (otherwise there's a conflict of
421 431 # singletons). Ultimately the solution to this problem is to refactor our
422 432 # assumptions about what needs to be a singleton and what doesn't (app
423 433 # objects should, individual shells shouldn't). But for now, this
424 434 # workaround allows the test suite for the inprocess module to complete.
425 435 if 'kernel.inprocess' not in section.name:
426 436 from IPython.testing import globalipapp
427 437 globalipapp.start_ipython()
428 438
429 439 # Now nose can run
430 440 TestProgram(argv=argv, addplugins=plugins)
431 441
432 442 if __name__ == '__main__':
433 443 run_iptest()
General Comments 0
You need to be logged in to leave comments. Login now