##// END OF EJS Templates
Standardise some module docstring first lines
Thomas Kluyver -
Show More
@@ -1,535 +1,540 b''
1 """Input transformer classes to support IPython special syntax.
2
3 This includes the machinery to recognise and transform ``%magic`` commands,
4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
5 """
1 6 import abc
2 7 import functools
3 8 import re
4 9
5 10 from IPython.core.splitinput import LineInfo
6 11 from IPython.utils import tokenize2
7 12 from IPython.utils.openpy import cookie_comment_re
8 13 from IPython.utils.py3compat import with_metaclass, PY3
9 14 from IPython.utils.tokenize2 import generate_tokens, untokenize, TokenError
10 15
11 16 if PY3:
12 17 from io import StringIO
13 18 else:
14 19 from StringIO import StringIO
15 20
16 21 #-----------------------------------------------------------------------------
17 22 # Globals
18 23 #-----------------------------------------------------------------------------
19 24
20 25 # The escape sequences that define the syntax transformations IPython will
21 26 # apply to user input. These can NOT be just changed here: many regular
22 27 # expressions and other parts of the code may use their hardcoded values, and
23 28 # for all intents and purposes they constitute the 'IPython syntax', so they
24 29 # should be considered fixed.
25 30
26 31 ESC_SHELL = '!' # Send line to underlying system shell
27 32 ESC_SH_CAP = '!!' # Send line to system shell and capture output
28 33 ESC_HELP = '?' # Find information about object
29 34 ESC_HELP2 = '??' # Find extra-detailed information about object
30 35 ESC_MAGIC = '%' # Call magic function
31 36 ESC_MAGIC2 = '%%' # Call cell-magic function
32 37 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
33 38 ESC_QUOTE2 = ';' # Quote all args as a single string, call
34 39 ESC_PAREN = '/' # Call first argument with rest of line as arguments
35 40
36 41 ESC_SEQUENCES = [ESC_SHELL, ESC_SH_CAP, ESC_HELP ,\
37 42 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2,\
38 43 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN ]
39 44
40 45
41 46 class InputTransformer(with_metaclass(abc.ABCMeta, object)):
42 47 """Abstract base class for line-based input transformers."""
43 48
44 49 @abc.abstractmethod
45 50 def push(self, line):
46 51 """Send a line of input to the transformer, returning the transformed
47 52 input or None if the transformer is waiting for more input.
48 53
49 54 Must be overridden by subclasses.
50 55 """
51 56 pass
52 57
53 58 @abc.abstractmethod
54 59 def reset(self):
55 60 """Return, transformed any lines that the transformer has accumulated,
56 61 and reset its internal state.
57 62
58 63 Must be overridden by subclasses.
59 64 """
60 65 pass
61 66
62 67 @classmethod
63 68 def wrap(cls, func):
64 69 """Can be used by subclasses as a decorator, to return a factory that
65 70 will allow instantiation with the decorated object.
66 71 """
67 72 @functools.wraps(func)
68 73 def transformer_factory(**kwargs):
69 74 return cls(func, **kwargs)
70 75
71 76 return transformer_factory
72 77
73 78 class StatelessInputTransformer(InputTransformer):
74 79 """Wrapper for a stateless input transformer implemented as a function."""
75 80 def __init__(self, func):
76 81 self.func = func
77 82
78 83 def __repr__(self):
79 84 return "StatelessInputTransformer(func={0!r})".format(self.func)
80 85
81 86 def push(self, line):
82 87 """Send a line of input to the transformer, returning the
83 88 transformed input."""
84 89 return self.func(line)
85 90
86 91 def reset(self):
87 92 """No-op - exists for compatibility."""
88 93 pass
89 94
90 95 class CoroutineInputTransformer(InputTransformer):
91 96 """Wrapper for an input transformer implemented as a coroutine."""
92 97 def __init__(self, coro, **kwargs):
93 98 # Prime it
94 99 self.coro = coro(**kwargs)
95 100 next(self.coro)
96 101
97 102 def __repr__(self):
98 103 return "CoroutineInputTransformer(coro={0!r})".format(self.coro)
99 104
100 105 def push(self, line):
101 106 """Send a line of input to the transformer, returning the
102 107 transformed input or None if the transformer is waiting for more
103 108 input.
104 109 """
105 110 return self.coro.send(line)
106 111
107 112 def reset(self):
108 113 """Return, transformed any lines that the transformer has
109 114 accumulated, and reset its internal state.
110 115 """
111 116 return self.coro.send(None)
112 117
113 118 class TokenInputTransformer(InputTransformer):
114 119 """Wrapper for a token-based input transformer.
115 120
116 121 func should accept a list of tokens (5-tuples, see tokenize docs), and
117 122 return an iterable which can be passed to tokenize.untokenize().
118 123 """
119 124 def __init__(self, func):
120 125 self.func = func
121 126 self.current_line = ""
122 127 self.line_used = False
123 128 self.reset_tokenizer()
124 129
125 130 def reset_tokenizer(self):
126 131 self.tokenizer = generate_tokens(self.get_line)
127 132
128 133 def get_line(self):
129 134 if self.line_used:
130 135 raise TokenError
131 136 self.line_used = True
132 137 return self.current_line
133 138
134 139 def push(self, line):
135 140 self.current_line += line + "\n"
136 141 if self.current_line.isspace():
137 142 return self.reset()
138 143
139 144 self.line_used = False
140 145 tokens = []
141 146 stop_at_NL = False
142 147 try:
143 148 for intok in self.tokenizer:
144 149 tokens.append(intok)
145 150 t = intok[0]
146 151 if t == tokenize2.NEWLINE or (stop_at_NL and t == tokenize2.NL):
147 152 # Stop before we try to pull a line we don't have yet
148 153 break
149 154 elif t == tokenize2.ERRORTOKEN:
150 155 stop_at_NL = True
151 156 except TokenError:
152 157 # Multi-line statement - stop and try again with the next line
153 158 self.reset_tokenizer()
154 159 return None
155 160
156 161 return self.output(tokens)
157 162
158 163 def output(self, tokens):
159 164 self.current_line = ""
160 165 self.reset_tokenizer()
161 166 return untokenize(self.func(tokens)).rstrip('\n')
162 167
163 168 def reset(self):
164 169 l = self.current_line
165 170 self.current_line = ""
166 171 self.reset_tokenizer()
167 172 if l:
168 173 return l.rstrip('\n')
169 174
170 175 class assemble_python_lines(TokenInputTransformer):
171 176 def __init__(self):
172 177 super(assemble_python_lines, self).__init__(None)
173 178
174 179 def output(self, tokens):
175 180 return self.reset()
176 181
177 182 @CoroutineInputTransformer.wrap
178 183 def assemble_logical_lines():
179 184 """Join lines following explicit line continuations (\)"""
180 185 line = ''
181 186 while True:
182 187 line = (yield line)
183 188 if not line or line.isspace():
184 189 continue
185 190
186 191 parts = []
187 192 while line is not None:
188 193 if line.endswith('\\') and (not has_comment(line)):
189 194 parts.append(line[:-1])
190 195 line = (yield None) # Get another line
191 196 else:
192 197 parts.append(line)
193 198 break
194 199
195 200 # Output
196 201 line = ''.join(parts)
197 202
198 203 # Utilities
199 204 def _make_help_call(target, esc, lspace, next_input=None):
200 205 """Prepares a pinfo(2)/psearch call from a target name and the escape
201 206 (i.e. ? or ??)"""
202 207 method = 'pinfo2' if esc == '??' \
203 208 else 'psearch' if '*' in target \
204 209 else 'pinfo'
205 210 arg = " ".join([method, target])
206 211 if next_input is None:
207 212 return '%sget_ipython().magic(%r)' % (lspace, arg)
208 213 else:
209 214 return '%sget_ipython().set_next_input(%r);get_ipython().magic(%r)' % \
210 215 (lspace, next_input, arg)
211 216
212 217 # These define the transformations for the different escape characters.
213 218 def _tr_system(line_info):
214 219 "Translate lines escaped with: !"
215 220 cmd = line_info.line.lstrip().lstrip(ESC_SHELL)
216 221 return '%sget_ipython().system(%r)' % (line_info.pre, cmd)
217 222
218 223 def _tr_system2(line_info):
219 224 "Translate lines escaped with: !!"
220 225 cmd = line_info.line.lstrip()[2:]
221 226 return '%sget_ipython().getoutput(%r)' % (line_info.pre, cmd)
222 227
223 228 def _tr_help(line_info):
224 229 "Translate lines escaped with: ?/??"
225 230 # A naked help line should just fire the intro help screen
226 231 if not line_info.line[1:]:
227 232 return 'get_ipython().show_usage()'
228 233
229 234 return _make_help_call(line_info.ifun, line_info.esc, line_info.pre)
230 235
231 236 def _tr_magic(line_info):
232 237 "Translate lines escaped with: %"
233 238 tpl = '%sget_ipython().magic(%r)'
234 239 if line_info.line.startswith(ESC_MAGIC2):
235 240 return line_info.line
236 241 cmd = ' '.join([line_info.ifun, line_info.the_rest]).strip()
237 242 return tpl % (line_info.pre, cmd)
238 243
239 244 def _tr_quote(line_info):
240 245 "Translate lines escaped with: ,"
241 246 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
242 247 '", "'.join(line_info.the_rest.split()) )
243 248
244 249 def _tr_quote2(line_info):
245 250 "Translate lines escaped with: ;"
246 251 return '%s%s("%s")' % (line_info.pre, line_info.ifun,
247 252 line_info.the_rest)
248 253
249 254 def _tr_paren(line_info):
250 255 "Translate lines escaped with: /"
251 256 return '%s%s(%s)' % (line_info.pre, line_info.ifun,
252 257 ", ".join(line_info.the_rest.split()))
253 258
254 259 tr = { ESC_SHELL : _tr_system,
255 260 ESC_SH_CAP : _tr_system2,
256 261 ESC_HELP : _tr_help,
257 262 ESC_HELP2 : _tr_help,
258 263 ESC_MAGIC : _tr_magic,
259 264 ESC_QUOTE : _tr_quote,
260 265 ESC_QUOTE2 : _tr_quote2,
261 266 ESC_PAREN : _tr_paren }
262 267
263 268 @StatelessInputTransformer.wrap
264 269 def escaped_commands(line):
265 270 """Transform escaped commands - %magic, !system, ?help + various autocalls.
266 271 """
267 272 if not line or line.isspace():
268 273 return line
269 274 lineinf = LineInfo(line)
270 275 if lineinf.esc not in tr:
271 276 return line
272 277
273 278 return tr[lineinf.esc](lineinf)
274 279
275 280 _initial_space_re = re.compile(r'\s*')
276 281
277 282 _help_end_re = re.compile(r"""(%{0,2}
278 283 [a-zA-Z_*][\w*]* # Variable name
279 284 (\.[a-zA-Z_*][\w*]*)* # .etc.etc
280 285 )
281 286 (\?\??)$ # ? or ??
282 287 """,
283 288 re.VERBOSE)
284 289
285 290 # Extra pseudotokens for multiline strings and data structures
286 291 _MULTILINE_STRING = object()
287 292 _MULTILINE_STRUCTURE = object()
288 293
289 294 def _line_tokens(line):
290 295 """Helper for has_comment and ends_in_comment_or_string."""
291 296 readline = StringIO(line).readline
292 297 toktypes = set()
293 298 try:
294 299 for t in generate_tokens(readline):
295 300 toktypes.add(t[0])
296 301 except TokenError as e:
297 302 # There are only two cases where a TokenError is raised.
298 303 if 'multi-line string' in e.args[0]:
299 304 toktypes.add(_MULTILINE_STRING)
300 305 else:
301 306 toktypes.add(_MULTILINE_STRUCTURE)
302 307 return toktypes
303 308
304 309 def has_comment(src):
305 310 """Indicate whether an input line has (i.e. ends in, or is) a comment.
306 311
307 312 This uses tokenize, so it can distinguish comments from # inside strings.
308 313
309 314 Parameters
310 315 ----------
311 316 src : string
312 317 A single line input string.
313 318
314 319 Returns
315 320 -------
316 321 comment : bool
317 322 True if source has a comment.
318 323 """
319 324 return (tokenize2.COMMENT in _line_tokens(src))
320 325
321 326 def ends_in_comment_or_string(src):
322 327 """Indicates whether or not an input line ends in a comment or within
323 328 a multiline string.
324 329
325 330 Parameters
326 331 ----------
327 332 src : string
328 333 A single line input string.
329 334
330 335 Returns
331 336 -------
332 337 comment : bool
333 338 True if source ends in a comment or multiline string.
334 339 """
335 340 toktypes = _line_tokens(src)
336 341 return (tokenize2.COMMENT in toktypes) or (_MULTILINE_STRING in toktypes)
337 342
338 343
339 344 @StatelessInputTransformer.wrap
340 345 def help_end(line):
341 346 """Translate lines with ?/?? at the end"""
342 347 m = _help_end_re.search(line)
343 348 if m is None or ends_in_comment_or_string(line):
344 349 return line
345 350 target = m.group(1)
346 351 esc = m.group(3)
347 352 lspace = _initial_space_re.match(line).group(0)
348 353
349 354 # If we're mid-command, put it back on the next prompt for the user.
350 355 next_input = line.rstrip('?') if line.strip() != m.group(0) else None
351 356
352 357 return _make_help_call(target, esc, lspace, next_input)
353 358
354 359
355 360 @CoroutineInputTransformer.wrap
356 361 def cellmagic(end_on_blank_line=False):
357 362 """Captures & transforms cell magics.
358 363
359 364 After a cell magic is started, this stores up any lines it gets until it is
360 365 reset (sent None).
361 366 """
362 367 tpl = 'get_ipython().run_cell_magic(%r, %r, %r)'
363 368 cellmagic_help_re = re.compile('%%\w+\?')
364 369 line = ''
365 370 while True:
366 371 line = (yield line)
367 372 # consume leading empty lines
368 373 while not line:
369 374 line = (yield line)
370 375
371 376 if not line.startswith(ESC_MAGIC2):
372 377 # This isn't a cell magic, idle waiting for reset then start over
373 378 while line is not None:
374 379 line = (yield line)
375 380 continue
376 381
377 382 if cellmagic_help_re.match(line):
378 383 # This case will be handled by help_end
379 384 continue
380 385
381 386 first = line
382 387 body = []
383 388 line = (yield None)
384 389 while (line is not None) and \
385 390 ((line.strip() != '') or not end_on_blank_line):
386 391 body.append(line)
387 392 line = (yield None)
388 393
389 394 # Output
390 395 magic_name, _, first = first.partition(' ')
391 396 magic_name = magic_name.lstrip(ESC_MAGIC2)
392 397 line = tpl % (magic_name, first, u'\n'.join(body))
393 398
394 399
395 400 def _strip_prompts(prompt_re, initial_re=None):
396 401 """Remove matching input prompts from a block of input.
397 402
398 403 Parameters
399 404 ----------
400 405 prompt_re : regular expression
401 406 A regular expression matching any input prompt (including continuation)
402 407 initial_re : regular expression, optional
403 408 A regular expression matching only the initial prompt, but not continuation.
404 409 If no initial expression is given, prompt_re will be used everywhere.
405 410 Used mainly for plain Python prompts, where the continuation prompt
406 411 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
407 412
408 413 If initial_re and prompt_re differ,
409 414 only initial_re will be tested against the first line.
410 415 If any prompt is found on the first two lines,
411 416 prompts will be stripped from the rest of the block.
412 417 """
413 418 if initial_re is None:
414 419 initial_re = prompt_re
415 420 line = ''
416 421 while True:
417 422 line = (yield line)
418 423
419 424 # First line of cell
420 425 if line is None:
421 426 continue
422 427 out, n1 = initial_re.subn('', line, count=1)
423 428 line = (yield out)
424 429
425 430 if line is None:
426 431 continue
427 432 # check for any prompt on the second line of the cell,
428 433 # because people often copy from just after the first prompt,
429 434 # so we might not see it in the first line.
430 435 out, n2 = prompt_re.subn('', line, count=1)
431 436 line = (yield out)
432 437
433 438 if n1 or n2:
434 439 # Found a prompt in the first two lines - check for it in
435 440 # the rest of the cell as well.
436 441 while line is not None:
437 442 line = (yield prompt_re.sub('', line, count=1))
438 443
439 444 else:
440 445 # Prompts not in input - wait for reset
441 446 while line is not None:
442 447 line = (yield line)
443 448
444 449 @CoroutineInputTransformer.wrap
445 450 def classic_prompt():
446 451 """Strip the >>>/... prompts of the Python interactive shell."""
447 452 # FIXME: non-capturing version (?:...) usable?
448 453 prompt_re = re.compile(r'^(>>> ?|\.\.\. ?)')
449 454 initial_re = re.compile(r'^(>>> ?)')
450 455 return _strip_prompts(prompt_re, initial_re)
451 456
452 457 @CoroutineInputTransformer.wrap
453 458 def ipy_prompt():
454 459 """Strip IPython's In [1]:/...: prompts."""
455 460 # FIXME: non-capturing version (?:...) usable?
456 461 # FIXME: r'^(In \[\d+\]: | {3}\.{3,}: )' clearer?
457 462 prompt_re = re.compile(r'^(In \[\d+\]: |\ \ \ \.\.\.+: )')
458 463 return _strip_prompts(prompt_re)
459 464
460 465
461 466 @CoroutineInputTransformer.wrap
462 467 def leading_indent():
463 468 """Remove leading indentation.
464 469
465 470 If the first line starts with a spaces or tabs, the same whitespace will be
466 471 removed from each following line until it is reset.
467 472 """
468 473 space_re = re.compile(r'^[ \t]+')
469 474 line = ''
470 475 while True:
471 476 line = (yield line)
472 477
473 478 if line is None:
474 479 continue
475 480
476 481 m = space_re.match(line)
477 482 if m:
478 483 space = m.group(0)
479 484 while line is not None:
480 485 if line.startswith(space):
481 486 line = line[len(space):]
482 487 line = (yield line)
483 488 else:
484 489 # No leading spaces - wait for reset
485 490 while line is not None:
486 491 line = (yield line)
487 492
488 493
489 494 @CoroutineInputTransformer.wrap
490 495 def strip_encoding_cookie():
491 496 """Remove encoding comment if found in first two lines
492 497
493 498 If the first or second line has the `# coding: utf-8` comment,
494 499 it will be removed.
495 500 """
496 501 line = ''
497 502 while True:
498 503 line = (yield line)
499 504 # check comment on first two lines
500 505 for i in range(2):
501 506 if line is None:
502 507 break
503 508 if cookie_comment_re.match(line):
504 509 line = (yield "")
505 510 else:
506 511 line = (yield line)
507 512
508 513 # no-op on the rest of the cell
509 514 while line is not None:
510 515 line = (yield line)
511 516
512 517
513 518 assign_system_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
514 519 r'\s*=\s*!\s*(?P<cmd>.*)')
515 520 assign_system_template = '%s = get_ipython().getoutput(%r)'
516 521 @StatelessInputTransformer.wrap
517 522 def assign_from_system(line):
518 523 """Transform assignment from system commands (e.g. files = !ls)"""
519 524 m = assign_system_re.match(line)
520 525 if m is None:
521 526 return line
522 527
523 528 return assign_system_template % m.group('lhs', 'cmd')
524 529
525 530 assign_magic_re = re.compile(r'(?P<lhs>(\s*)([\w\.]+)((\s*,\s*[\w\.]+)*))'
526 531 r'\s*=\s*%\s*(?P<cmd>.*)')
527 532 assign_magic_template = '%s = get_ipython().magic(%r)'
528 533 @StatelessInputTransformer.wrap
529 534 def assign_from_magic(line):
530 535 """Transform assignment from magic commands (e.g. a = %who_ls)"""
531 536 m = assign_magic_re.match(line)
532 537 if m is None:
533 538 return line
534 539
535 540 return assign_magic_template % m.group('lhs', 'cmd')
@@ -1,1269 +1,1269 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 ultratb.py -- Spice up your tracebacks!
3 Verbose and colourful traceback formatting.
4 4
5 5 **ColorTB**
6 6
7 7 I've always found it a bit hard to visually parse tracebacks in Python. The
8 8 ColorTB class is a solution to that problem. It colors the different parts of a
9 9 traceback in a manner similar to what you would expect from a syntax-highlighting
10 10 text editor.
11 11
12 12 Installation instructions for ColorTB::
13 13
14 14 import sys,ultratb
15 15 sys.excepthook = ultratb.ColorTB()
16 16
17 17 **VerboseTB**
18 18
19 19 I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
20 20 of useful info when a traceback occurs. Ping originally had it spit out HTML
21 21 and intended it for CGI programmers, but why should they have all the fun? I
22 22 altered it to spit out colored text to the terminal. It's a bit overwhelming,
23 23 but kind of neat, and maybe useful for long-running programs that you believe
24 24 are bug-free. If a crash *does* occur in that type of program you want details.
25 25 Give it a shot--you'll love it or you'll hate it.
26 26
27 27 .. note::
28 28
29 29 The Verbose mode prints the variables currently visible where the exception
30 30 happened (shortening their strings if too long). This can potentially be
31 31 very slow, if you happen to have a huge data structure whose string
32 32 representation is complex to compute. Your computer may appear to freeze for
33 33 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
34 34 with Ctrl-C (maybe hitting it more than once).
35 35
36 36 If you encounter this kind of situation often, you may want to use the
37 37 Verbose_novars mode instead of the regular Verbose, which avoids formatting
38 38 variables (but otherwise includes the information and context given by
39 39 Verbose).
40 40
41 41
42 42 Installation instructions for ColorTB::
43 43
44 44 import sys,ultratb
45 45 sys.excepthook = ultratb.VerboseTB()
46 46
47 47 Note: Much of the code in this module was lifted verbatim from the standard
48 48 library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
49 49
50 50 Color schemes
51 51 -------------
52 52
53 53 The colors are defined in the class TBTools through the use of the
54 54 ColorSchemeTable class. Currently the following exist:
55 55
56 56 - NoColor: allows all of this module to be used in any terminal (the color
57 57 escapes are just dummy blank strings).
58 58
59 59 - Linux: is meant to look good in a terminal like the Linux console (black
60 60 or very dark background).
61 61
62 62 - LightBG: similar to Linux but swaps dark/light colors to be more readable
63 63 in light background terminals.
64 64
65 65 You can implement other color schemes easily, the syntax is fairly
66 66 self-explanatory. Please send back new schemes you develop to the author for
67 67 possible inclusion in future releases.
68 68
69 69 Inheritance diagram:
70 70
71 71 .. inheritance-diagram:: IPython.core.ultratb
72 72 :parts: 3
73 73 """
74 74
75 75 #*****************************************************************************
76 76 # Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
77 77 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
78 78 #
79 79 # Distributed under the terms of the BSD License. The full license is in
80 80 # the file COPYING, distributed as part of this software.
81 81 #*****************************************************************************
82 82
83 83 from __future__ import unicode_literals
84 84 from __future__ import print_function
85 85
86 86 import inspect
87 87 import keyword
88 88 import linecache
89 89 import os
90 90 import pydoc
91 91 import re
92 92 import sys
93 93 import time
94 94 import tokenize
95 95 import traceback
96 96 import types
97 97
98 98 try: # Python 2
99 99 generate_tokens = tokenize.generate_tokens
100 100 except AttributeError: # Python 3
101 101 generate_tokens = tokenize.tokenize
102 102
103 103 # For purposes of monkeypatching inspect to fix a bug in it.
104 104 from inspect import getsourcefile, getfile, getmodule,\
105 105 ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode
106 106
107 107 # IPython's own modules
108 108 # Modified pdb which doesn't damage IPython's readline handling
109 109 from IPython import get_ipython
110 110 from IPython.core import debugger
111 111 from IPython.core.display_trap import DisplayTrap
112 112 from IPython.core.excolors import exception_colors
113 113 from IPython.utils import PyColorize
114 114 from IPython.utils import io
115 115 from IPython.utils import openpy
116 116 from IPython.utils import path as util_path
117 117 from IPython.utils import py3compat
118 118 from IPython.utils import ulinecache
119 119 from IPython.utils.data import uniq_stable
120 120 from IPython.utils.warn import info, error
121 121
122 122 # Globals
123 123 # amount of space to put line numbers before verbose tracebacks
124 124 INDENT_SIZE = 8
125 125
126 126 # Default color scheme. This is used, for example, by the traceback
127 127 # formatter. When running in an actual IPython instance, the user's rc.colors
128 128 # value is used, but havinga module global makes this functionality available
129 129 # to users of ultratb who are NOT running inside ipython.
130 130 DEFAULT_SCHEME = 'NoColor'
131 131
132 132 #---------------------------------------------------------------------------
133 133 # Code begins
134 134
135 135 # Utility functions
136 136 def inspect_error():
137 137 """Print a message about internal inspect errors.
138 138
139 139 These are unfortunately quite common."""
140 140
141 141 error('Internal Python error in the inspect module.\n'
142 142 'Below is the traceback from this internal error.\n')
143 143
144 144 # This function is a monkeypatch we apply to the Python inspect module. We have
145 145 # now found when it's needed (see discussion on issue gh-1456), and we have a
146 146 # test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if
147 147 # the monkeypatch is not applied. TK, Aug 2012.
148 148 def findsource(object):
149 149 """Return the entire source file and starting line number for an object.
150 150
151 151 The argument may be a module, class, method, function, traceback, frame,
152 152 or code object. The source code is returned as a list of all the lines
153 153 in the file and the line number indexes a line in that list. An IOError
154 154 is raised if the source code cannot be retrieved.
155 155
156 156 FIXED version with which we monkeypatch the stdlib to work around a bug."""
157 157
158 158 file = getsourcefile(object) or getfile(object)
159 159 # If the object is a frame, then trying to get the globals dict from its
160 160 # module won't work. Instead, the frame object itself has the globals
161 161 # dictionary.
162 162 globals_dict = None
163 163 if inspect.isframe(object):
164 164 # XXX: can this ever be false?
165 165 globals_dict = object.f_globals
166 166 else:
167 167 module = getmodule(object, file)
168 168 if module:
169 169 globals_dict = module.__dict__
170 170 lines = linecache.getlines(file, globals_dict)
171 171 if not lines:
172 172 raise IOError('could not get source code')
173 173
174 174 if ismodule(object):
175 175 return lines, 0
176 176
177 177 if isclass(object):
178 178 name = object.__name__
179 179 pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
180 180 # make some effort to find the best matching class definition:
181 181 # use the one with the least indentation, which is the one
182 182 # that's most probably not inside a function definition.
183 183 candidates = []
184 184 for i in range(len(lines)):
185 185 match = pat.match(lines[i])
186 186 if match:
187 187 # if it's at toplevel, it's already the best one
188 188 if lines[i][0] == 'c':
189 189 return lines, i
190 190 # else add whitespace to candidate list
191 191 candidates.append((match.group(1), i))
192 192 if candidates:
193 193 # this will sort by whitespace, and by line number,
194 194 # less whitespace first
195 195 candidates.sort()
196 196 return lines, candidates[0][1]
197 197 else:
198 198 raise IOError('could not find class definition')
199 199
200 200 if ismethod(object):
201 201 object = object.__func__
202 202 if isfunction(object):
203 203 object = object.__code__
204 204 if istraceback(object):
205 205 object = object.tb_frame
206 206 if isframe(object):
207 207 object = object.f_code
208 208 if iscode(object):
209 209 if not hasattr(object, 'co_firstlineno'):
210 210 raise IOError('could not find function definition')
211 211 pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
212 212 pmatch = pat.match
213 213 # fperez - fix: sometimes, co_firstlineno can give a number larger than
214 214 # the length of lines, which causes an error. Safeguard against that.
215 215 lnum = min(object.co_firstlineno,len(lines))-1
216 216 while lnum > 0:
217 217 if pmatch(lines[lnum]): break
218 218 lnum -= 1
219 219
220 220 return lines, lnum
221 221 raise IOError('could not find code object')
222 222
223 223 # Monkeypatch inspect to apply our bugfix. This code only works with Python >= 2.5
224 224 inspect.findsource = findsource
225 225
226 226 def fix_frame_records_filenames(records):
227 227 """Try to fix the filenames in each record from inspect.getinnerframes().
228 228
229 229 Particularly, modules loaded from within zip files have useless filenames
230 230 attached to their code object, and inspect.getinnerframes() just uses it.
231 231 """
232 232 fixed_records = []
233 233 for frame, filename, line_no, func_name, lines, index in records:
234 234 # Look inside the frame's globals dictionary for __file__, which should
235 235 # be better.
236 236 better_fn = frame.f_globals.get('__file__', None)
237 237 if isinstance(better_fn, str):
238 238 # Check the type just in case someone did something weird with
239 239 # __file__. It might also be None if the error occurred during
240 240 # import.
241 241 filename = better_fn
242 242 fixed_records.append((frame, filename, line_no, func_name, lines, index))
243 243 return fixed_records
244 244
245 245
246 246 def _fixed_getinnerframes(etb, context=1,tb_offset=0):
247 247 LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5
248 248
249 249 records = fix_frame_records_filenames(inspect.getinnerframes(etb, context))
250 250
251 251 # If the error is at the console, don't build any context, since it would
252 252 # otherwise produce 5 blank lines printed out (there is no file at the
253 253 # console)
254 254 rec_check = records[tb_offset:]
255 255 try:
256 256 rname = rec_check[0][1]
257 257 if rname == '<ipython console>' or rname.endswith('<string>'):
258 258 return rec_check
259 259 except IndexError:
260 260 pass
261 261
262 262 aux = traceback.extract_tb(etb)
263 263 assert len(records) == len(aux)
264 264 for i, (file, lnum, _, _) in zip(range(len(records)), aux):
265 265 maybeStart = lnum-1 - context//2
266 266 start = max(maybeStart, 0)
267 267 end = start + context
268 268 lines = ulinecache.getlines(file)[start:end]
269 269 buf = list(records[i])
270 270 buf[LNUM_POS] = lnum
271 271 buf[INDEX_POS] = lnum - 1 - start
272 272 buf[LINES_POS] = lines
273 273 records[i] = tuple(buf)
274 274 return records[tb_offset:]
275 275
276 276 # Helper function -- largely belongs to VerboseTB, but we need the same
277 277 # functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
278 278 # can be recognized properly by ipython.el's py-traceback-line-re
279 279 # (SyntaxErrors have to be treated specially because they have no traceback)
280 280
281 281 _parser = PyColorize.Parser()
282 282
283 283 def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None):
284 284 numbers_width = INDENT_SIZE - 1
285 285 res = []
286 286 i = lnum - index
287 287
288 288 # This lets us get fully syntax-highlighted tracebacks.
289 289 if scheme is None:
290 290 ipinst = get_ipython()
291 291 if ipinst is not None:
292 292 scheme = ipinst.colors
293 293 else:
294 294 scheme = DEFAULT_SCHEME
295 295
296 296 _line_format = _parser.format2
297 297
298 298 for line in lines:
299 299 line = py3compat.cast_unicode(line)
300 300
301 301 new_line, err = _line_format(line, 'str', scheme)
302 302 if not err: line = new_line
303 303
304 304 if i == lnum:
305 305 # This is the line with the error
306 306 pad = numbers_width - len(str(i))
307 307 if pad >= 3:
308 308 marker = '-'*(pad-3) + '-> '
309 309 elif pad == 2:
310 310 marker = '> '
311 311 elif pad == 1:
312 312 marker = '>'
313 313 else:
314 314 marker = ''
315 315 num = marker + str(i)
316 316 line = '%s%s%s %s%s' %(Colors.linenoEm, num,
317 317 Colors.line, line, Colors.Normal)
318 318 else:
319 319 num = '%*s' % (numbers_width,i)
320 320 line = '%s%s%s %s' %(Colors.lineno, num,
321 321 Colors.Normal, line)
322 322
323 323 res.append(line)
324 324 if lvals and i == lnum:
325 325 res.append(lvals + '\n')
326 326 i = i + 1
327 327 return res
328 328
329 329
330 330 #---------------------------------------------------------------------------
331 331 # Module classes
332 332 class TBTools(object):
333 333 """Basic tools used by all traceback printer classes."""
334 334
335 335 # Number of frames to skip when reporting tracebacks
336 336 tb_offset = 0
337 337
338 338 def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None):
339 339 # Whether to call the interactive pdb debugger after printing
340 340 # tracebacks or not
341 341 self.call_pdb = call_pdb
342 342
343 343 # Output stream to write to. Note that we store the original value in
344 344 # a private attribute and then make the public ostream a property, so
345 345 # that we can delay accessing io.stdout until runtime. The way
346 346 # things are written now, the io.stdout object is dynamically managed
347 347 # so a reference to it should NEVER be stored statically. This
348 348 # property approach confines this detail to a single location, and all
349 349 # subclasses can simply access self.ostream for writing.
350 350 self._ostream = ostream
351 351
352 352 # Create color table
353 353 self.color_scheme_table = exception_colors()
354 354
355 355 self.set_colors(color_scheme)
356 356 self.old_scheme = color_scheme # save initial value for toggles
357 357
358 358 if call_pdb:
359 359 self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name)
360 360 else:
361 361 self.pdb = None
362 362
363 363 def _get_ostream(self):
364 364 """Output stream that exceptions are written to.
365 365
366 366 Valid values are:
367 367
368 368 - None: the default, which means that IPython will dynamically resolve
369 369 to io.stdout. This ensures compatibility with most tools, including
370 370 Windows (where plain stdout doesn't recognize ANSI escapes).
371 371
372 372 - Any object with 'write' and 'flush' attributes.
373 373 """
374 374 return io.stdout if self._ostream is None else self._ostream
375 375
376 376 def _set_ostream(self, val):
377 377 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
378 378 self._ostream = val
379 379
380 380 ostream = property(_get_ostream, _set_ostream)
381 381
382 382 def set_colors(self,*args,**kw):
383 383 """Shorthand access to the color table scheme selector method."""
384 384
385 385 # Set own color table
386 386 self.color_scheme_table.set_active_scheme(*args,**kw)
387 387 # for convenience, set Colors to the active scheme
388 388 self.Colors = self.color_scheme_table.active_colors
389 389 # Also set colors of debugger
390 390 if hasattr(self,'pdb') and self.pdb is not None:
391 391 self.pdb.set_colors(*args,**kw)
392 392
393 393 def color_toggle(self):
394 394 """Toggle between the currently active color scheme and NoColor."""
395 395
396 396 if self.color_scheme_table.active_scheme_name == 'NoColor':
397 397 self.color_scheme_table.set_active_scheme(self.old_scheme)
398 398 self.Colors = self.color_scheme_table.active_colors
399 399 else:
400 400 self.old_scheme = self.color_scheme_table.active_scheme_name
401 401 self.color_scheme_table.set_active_scheme('NoColor')
402 402 self.Colors = self.color_scheme_table.active_colors
403 403
404 404 def stb2text(self, stb):
405 405 """Convert a structured traceback (a list) to a string."""
406 406 return '\n'.join(stb)
407 407
408 408 def text(self, etype, value, tb, tb_offset=None, context=5):
409 409 """Return formatted traceback.
410 410
411 411 Subclasses may override this if they add extra arguments.
412 412 """
413 413 tb_list = self.structured_traceback(etype, value, tb,
414 414 tb_offset, context)
415 415 return self.stb2text(tb_list)
416 416
417 417 def structured_traceback(self, etype, evalue, tb, tb_offset=None,
418 418 context=5, mode=None):
419 419 """Return a list of traceback frames.
420 420
421 421 Must be implemented by each class.
422 422 """
423 423 raise NotImplementedError()
424 424
425 425
426 426 #---------------------------------------------------------------------------
427 427 class ListTB(TBTools):
428 428 """Print traceback information from a traceback list, with optional color.
429 429
430 430 Calling requires 3 arguments: (etype, evalue, elist)
431 431 as would be obtained by::
432 432
433 433 etype, evalue, tb = sys.exc_info()
434 434 if tb:
435 435 elist = traceback.extract_tb(tb)
436 436 else:
437 437 elist = None
438 438
439 439 It can thus be used by programs which need to process the traceback before
440 440 printing (such as console replacements based on the code module from the
441 441 standard library).
442 442
443 443 Because they are meant to be called without a full traceback (only a
444 444 list), instances of this class can't call the interactive pdb debugger."""
445 445
446 446 def __init__(self,color_scheme = 'NoColor', call_pdb=False, ostream=None):
447 447 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
448 448 ostream=ostream)
449 449
450 450 def __call__(self, etype, value, elist):
451 451 self.ostream.flush()
452 452 self.ostream.write(self.text(etype, value, elist))
453 453 self.ostream.write('\n')
454 454
455 455 def structured_traceback(self, etype, value, elist, tb_offset=None,
456 456 context=5):
457 457 """Return a color formatted string with the traceback info.
458 458
459 459 Parameters
460 460 ----------
461 461 etype : exception type
462 462 Type of the exception raised.
463 463
464 464 value : object
465 465 Data stored in the exception
466 466
467 467 elist : list
468 468 List of frames, see class docstring for details.
469 469
470 470 tb_offset : int, optional
471 471 Number of frames in the traceback to skip. If not given, the
472 472 instance value is used (set in constructor).
473 473
474 474 context : int, optional
475 475 Number of lines of context information to print.
476 476
477 477 Returns
478 478 -------
479 479 String with formatted exception.
480 480 """
481 481 tb_offset = self.tb_offset if tb_offset is None else tb_offset
482 482 Colors = self.Colors
483 483 out_list = []
484 484 if elist:
485 485
486 486 if tb_offset and len(elist) > tb_offset:
487 487 elist = elist[tb_offset:]
488 488
489 489 out_list.append('Traceback %s(most recent call last)%s:' %
490 490 (Colors.normalEm, Colors.Normal) + '\n')
491 491 out_list.extend(self._format_list(elist))
492 492 # The exception info should be a single entry in the list.
493 493 lines = ''.join(self._format_exception_only(etype, value))
494 494 out_list.append(lines)
495 495
496 496 # Note: this code originally read:
497 497
498 498 ## for line in lines[:-1]:
499 499 ## out_list.append(" "+line)
500 500 ## out_list.append(lines[-1])
501 501
502 502 # This means it was indenting everything but the last line by a little
503 503 # bit. I've disabled this for now, but if we see ugliness somewhre we
504 504 # can restore it.
505 505
506 506 return out_list
507 507
508 508 def _format_list(self, extracted_list):
509 509 """Format a list of traceback entry tuples for printing.
510 510
511 511 Given a list of tuples as returned by extract_tb() or
512 512 extract_stack(), return a list of strings ready for printing.
513 513 Each string in the resulting list corresponds to the item with the
514 514 same index in the argument list. Each string ends in a newline;
515 515 the strings may contain internal newlines as well, for those items
516 516 whose source text line is not None.
517 517
518 518 Lifted almost verbatim from traceback.py
519 519 """
520 520
521 521 Colors = self.Colors
522 522 list = []
523 523 for filename, lineno, name, line in extracted_list[:-1]:
524 524 item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \
525 525 (Colors.filename, filename, Colors.Normal,
526 526 Colors.lineno, lineno, Colors.Normal,
527 527 Colors.name, name, Colors.Normal)
528 528 if line:
529 529 item += ' %s\n' % line.strip()
530 530 list.append(item)
531 531 # Emphasize the last entry
532 532 filename, lineno, name, line = extracted_list[-1]
533 533 item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \
534 534 (Colors.normalEm,
535 535 Colors.filenameEm, filename, Colors.normalEm,
536 536 Colors.linenoEm, lineno, Colors.normalEm,
537 537 Colors.nameEm, name, Colors.normalEm,
538 538 Colors.Normal)
539 539 if line:
540 540 item += '%s %s%s\n' % (Colors.line, line.strip(),
541 541 Colors.Normal)
542 542 list.append(item)
543 543 #from pprint import pformat; print 'LISTTB', pformat(list) # dbg
544 544 return list
545 545
546 546 def _format_exception_only(self, etype, value):
547 547 """Format the exception part of a traceback.
548 548
549 549 The arguments are the exception type and value such as given by
550 550 sys.exc_info()[:2]. The return value is a list of strings, each ending
551 551 in a newline. Normally, the list contains a single string; however,
552 552 for SyntaxError exceptions, it contains several lines that (when
553 553 printed) display detailed information about where the syntax error
554 554 occurred. The message indicating which exception occurred is the
555 555 always last string in the list.
556 556
557 557 Also lifted nearly verbatim from traceback.py
558 558 """
559 559 have_filedata = False
560 560 Colors = self.Colors
561 561 list = []
562 562 stype = Colors.excName + etype.__name__ + Colors.Normal
563 563 if value is None:
564 564 # Not sure if this can still happen in Python 2.6 and above
565 565 list.append( py3compat.cast_unicode(stype) + '\n')
566 566 else:
567 567 if issubclass(etype, SyntaxError):
568 568 have_filedata = True
569 569 #print 'filename is',filename # dbg
570 570 if not value.filename: value.filename = "<string>"
571 571 if value.lineno:
572 572 lineno = value.lineno
573 573 textline = ulinecache.getline(value.filename, value.lineno)
574 574 else:
575 575 lineno = 'unknown'
576 576 textline = ''
577 577 list.append('%s File %s"%s"%s, line %s%s%s\n' % \
578 578 (Colors.normalEm,
579 579 Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm,
580 580 Colors.linenoEm, lineno, Colors.Normal ))
581 581 if textline == '':
582 582 textline = py3compat.cast_unicode(value.text, "utf-8")
583 583
584 584 if textline is not None:
585 585 i = 0
586 586 while i < len(textline) and textline[i].isspace():
587 587 i += 1
588 588 list.append('%s %s%s\n' % (Colors.line,
589 589 textline.strip(),
590 590 Colors.Normal))
591 591 if value.offset is not None:
592 592 s = ' '
593 593 for c in textline[i:value.offset-1]:
594 594 if c.isspace():
595 595 s += c
596 596 else:
597 597 s += ' '
598 598 list.append('%s%s^%s\n' % (Colors.caret, s,
599 599 Colors.Normal) )
600 600
601 601 try:
602 602 s = value.msg
603 603 except Exception:
604 604 s = self._some_str(value)
605 605 if s:
606 606 list.append('%s%s:%s %s\n' % (str(stype), Colors.excName,
607 607 Colors.Normal, s))
608 608 else:
609 609 list.append('%s\n' % str(stype))
610 610
611 611 # sync with user hooks
612 612 if have_filedata:
613 613 ipinst = get_ipython()
614 614 if ipinst is not None:
615 615 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
616 616
617 617 return list
618 618
619 619 def get_exception_only(self, etype, value):
620 620 """Only print the exception type and message, without a traceback.
621 621
622 622 Parameters
623 623 ----------
624 624 etype : exception type
625 625 value : exception value
626 626 """
627 627 return ListTB.structured_traceback(self, etype, value, [])
628 628
629 629
630 630 def show_exception_only(self, etype, evalue):
631 631 """Only print the exception type and message, without a traceback.
632 632
633 633 Parameters
634 634 ----------
635 635 etype : exception type
636 636 value : exception value
637 637 """
638 638 # This method needs to use __call__ from *this* class, not the one from
639 639 # a subclass whose signature or behavior may be different
640 640 ostream = self.ostream
641 641 ostream.flush()
642 642 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
643 643 ostream.flush()
644 644
645 645 def _some_str(self, value):
646 646 # Lifted from traceback.py
647 647 try:
648 648 return str(value)
649 649 except:
650 650 return '<unprintable %s object>' % type(value).__name__
651 651
652 652 #----------------------------------------------------------------------------
653 653 class VerboseTB(TBTools):
654 654 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
655 655 of HTML. Requires inspect and pydoc. Crazy, man.
656 656
657 657 Modified version which optionally strips the topmost entries from the
658 658 traceback, to be used with alternate interpreters (because their own code
659 659 would appear in the traceback)."""
660 660
661 661 def __init__(self,color_scheme = 'Linux', call_pdb=False, ostream=None,
662 662 tb_offset=0, long_header=False, include_vars=True,
663 663 check_cache=None):
664 664 """Specify traceback offset, headers and color scheme.
665 665
666 666 Define how many frames to drop from the tracebacks. Calling it with
667 667 tb_offset=1 allows use of this handler in interpreters which will have
668 668 their own code at the top of the traceback (VerboseTB will first
669 669 remove that frame before printing the traceback info)."""
670 670 TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
671 671 ostream=ostream)
672 672 self.tb_offset = tb_offset
673 673 self.long_header = long_header
674 674 self.include_vars = include_vars
675 675 # By default we use linecache.checkcache, but the user can provide a
676 676 # different check_cache implementation. This is used by the IPython
677 677 # kernel to provide tracebacks for interactive code that is cached,
678 678 # by a compiler instance that flushes the linecache but preserves its
679 679 # own code cache.
680 680 if check_cache is None:
681 681 check_cache = linecache.checkcache
682 682 self.check_cache = check_cache
683 683
684 684 def structured_traceback(self, etype, evalue, etb, tb_offset=None,
685 685 context=5):
686 686 """Return a nice text document describing the traceback."""
687 687
688 688 tb_offset = self.tb_offset if tb_offset is None else tb_offset
689 689
690 690 # some locals
691 691 try:
692 692 etype = etype.__name__
693 693 except AttributeError:
694 694 pass
695 695 Colors = self.Colors # just a shorthand + quicker name lookup
696 696 ColorsNormal = Colors.Normal # used a lot
697 697 col_scheme = self.color_scheme_table.active_scheme_name
698 698 indent = ' '*INDENT_SIZE
699 699 em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal)
700 700 undefined = '%sundefined%s' % (Colors.em, ColorsNormal)
701 701 exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal)
702 702
703 703 # some internal-use functions
704 704 def text_repr(value):
705 705 """Hopefully pretty robust repr equivalent."""
706 706 # this is pretty horrible but should always return *something*
707 707 try:
708 708 return pydoc.text.repr(value)
709 709 except KeyboardInterrupt:
710 710 raise
711 711 except:
712 712 try:
713 713 return repr(value)
714 714 except KeyboardInterrupt:
715 715 raise
716 716 except:
717 717 try:
718 718 # all still in an except block so we catch
719 719 # getattr raising
720 720 name = getattr(value, '__name__', None)
721 721 if name:
722 722 # ick, recursion
723 723 return text_repr(name)
724 724 klass = getattr(value, '__class__', None)
725 725 if klass:
726 726 return '%s instance' % text_repr(klass)
727 727 except KeyboardInterrupt:
728 728 raise
729 729 except:
730 730 return 'UNRECOVERABLE REPR FAILURE'
731 731 def eqrepr(value, repr=text_repr): return '=%s' % repr(value)
732 732 def nullrepr(value, repr=text_repr): return ''
733 733
734 734 # meat of the code begins
735 735 try:
736 736 etype = etype.__name__
737 737 except AttributeError:
738 738 pass
739 739
740 740 if self.long_header:
741 741 # Header with the exception type, python version, and date
742 742 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
743 743 date = time.ctime(time.time())
744 744
745 745 head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal,
746 746 exc, ' '*(75-len(str(etype))-len(pyver)),
747 747 pyver, date.rjust(75) )
748 748 head += "\nA problem occured executing Python code. Here is the sequence of function"\
749 749 "\ncalls leading up to the error, with the most recent (innermost) call last."
750 750 else:
751 751 # Simplified header
752 752 head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc,
753 753 'Traceback (most recent call last)'.\
754 754 rjust(75 - len(str(etype)) ) )
755 755 frames = []
756 756 # Flush cache before calling inspect. This helps alleviate some of the
757 757 # problems with python 2.3's inspect.py.
758 758 ##self.check_cache()
759 759 # Drop topmost frames if requested
760 760 try:
761 761 # Try the default getinnerframes and Alex's: Alex's fixes some
762 762 # problems, but it generates empty tracebacks for console errors
763 763 # (5 blanks lines) where none should be returned.
764 764 #records = inspect.getinnerframes(etb, context)[tb_offset:]
765 765 #print 'python records:', records # dbg
766 766 records = _fixed_getinnerframes(etb, context, tb_offset)
767 767 #print 'alex records:', records # dbg
768 768 except:
769 769
770 770 # FIXME: I've been getting many crash reports from python 2.3
771 771 # users, traceable to inspect.py. If I can find a small test-case
772 772 # to reproduce this, I should either write a better workaround or
773 773 # file a bug report against inspect (if that's the real problem).
774 774 # So far, I haven't been able to find an isolated example to
775 775 # reproduce the problem.
776 776 inspect_error()
777 777 traceback.print_exc(file=self.ostream)
778 778 info('\nUnfortunately, your original traceback can not be constructed.\n')
779 779 return ''
780 780
781 781 # build some color string templates outside these nested loops
782 782 tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal)
783 783 tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm,
784 784 ColorsNormal)
785 785 tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \
786 786 (Colors.vName, Colors.valEm, ColorsNormal)
787 787 tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal)
788 788 tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal,
789 789 Colors.vName, ColorsNormal)
790 790 tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal)
791 791 tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal)
792 792 tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line,
793 793 ColorsNormal)
794 794
795 795 # now, loop over all records printing context and info
796 796 abspath = os.path.abspath
797 797 for frame, file, lnum, func, lines, index in records:
798 798 #print '*** record:',file,lnum,func,lines,index # dbg
799 799 if not file:
800 800 file = '?'
801 801 elif not(file.startswith(str("<")) and file.endswith(str(">"))):
802 802 # Guess that filenames like <string> aren't real filenames, so
803 803 # don't call abspath on them.
804 804 try:
805 805 file = abspath(file)
806 806 except OSError:
807 807 # Not sure if this can still happen: abspath now works with
808 808 # file names like <string>
809 809 pass
810 810 file = py3compat.cast_unicode(file, util_path.fs_encoding)
811 811 link = tpl_link % file
812 812 args, varargs, varkw, locals = inspect.getargvalues(frame)
813 813
814 814 if func == '?':
815 815 call = ''
816 816 else:
817 817 # Decide whether to include variable details or not
818 818 var_repr = self.include_vars and eqrepr or nullrepr
819 819 try:
820 820 call = tpl_call % (func,inspect.formatargvalues(args,
821 821 varargs, varkw,
822 822 locals,formatvalue=var_repr))
823 823 except KeyError:
824 824 # This happens in situations like errors inside generator
825 825 # expressions, where local variables are listed in the
826 826 # line, but can't be extracted from the frame. I'm not
827 827 # 100% sure this isn't actually a bug in inspect itself,
828 828 # but since there's no info for us to compute with, the
829 829 # best we can do is report the failure and move on. Here
830 830 # we must *not* call any traceback construction again,
831 831 # because that would mess up use of %debug later on. So we
832 832 # simply report the failure and move on. The only
833 833 # limitation will be that this frame won't have locals
834 834 # listed in the call signature. Quite subtle problem...
835 835 # I can't think of a good way to validate this in a unit
836 836 # test, but running a script consisting of:
837 837 # dict( (k,v.strip()) for (k,v) in range(10) )
838 838 # will illustrate the error, if this exception catch is
839 839 # disabled.
840 840 call = tpl_call_fail % func
841 841
842 842 # Don't attempt to tokenize binary files.
843 843 if file.endswith(('.so', '.pyd', '.dll')):
844 844 frames.append('%s %s\n' % (link,call))
845 845 continue
846 846 elif file.endswith(('.pyc','.pyo')):
847 847 # Look up the corresponding source file.
848 848 file = openpy.source_from_cache(file)
849 849
850 850 def linereader(file=file, lnum=[lnum], getline=ulinecache.getline):
851 851 line = getline(file, lnum[0])
852 852 lnum[0] += 1
853 853 return line
854 854
855 855 # Build the list of names on this line of code where the exception
856 856 # occurred.
857 857 try:
858 858 names = []
859 859 name_cont = False
860 860
861 861 for token_type, token, start, end, line in generate_tokens(linereader):
862 862 # build composite names
863 863 if token_type == tokenize.NAME and token not in keyword.kwlist:
864 864 if name_cont:
865 865 # Continuation of a dotted name
866 866 try:
867 867 names[-1].append(token)
868 868 except IndexError:
869 869 names.append([token])
870 870 name_cont = False
871 871 else:
872 872 # Regular new names. We append everything, the caller
873 873 # will be responsible for pruning the list later. It's
874 874 # very tricky to try to prune as we go, b/c composite
875 875 # names can fool us. The pruning at the end is easy
876 876 # to do (or the caller can print a list with repeated
877 877 # names if so desired.
878 878 names.append([token])
879 879 elif token == '.':
880 880 name_cont = True
881 881 elif token_type == tokenize.NEWLINE:
882 882 break
883 883
884 884 except (IndexError, UnicodeDecodeError):
885 885 # signals exit of tokenizer
886 886 pass
887 887 except tokenize.TokenError as msg:
888 888 _m = ("An unexpected error occurred while tokenizing input\n"
889 889 "The following traceback may be corrupted or invalid\n"
890 890 "The error message is: %s\n" % msg)
891 891 error(_m)
892 892
893 893 # Join composite names (e.g. "dict.fromkeys")
894 894 names = ['.'.join(n) for n in names]
895 895 # prune names list of duplicates, but keep the right order
896 896 unique_names = uniq_stable(names)
897 897
898 898 # Start loop over vars
899 899 lvals = []
900 900 if self.include_vars:
901 901 for name_full in unique_names:
902 902 name_base = name_full.split('.',1)[0]
903 903 if name_base in frame.f_code.co_varnames:
904 904 if name_base in locals:
905 905 try:
906 906 value = repr(eval(name_full,locals))
907 907 except:
908 908 value = undefined
909 909 else:
910 910 value = undefined
911 911 name = tpl_local_var % name_full
912 912 else:
913 913 if name_base in frame.f_globals:
914 914 try:
915 915 value = repr(eval(name_full,frame.f_globals))
916 916 except:
917 917 value = undefined
918 918 else:
919 919 value = undefined
920 920 name = tpl_global_var % name_full
921 921 lvals.append(tpl_name_val % (name,value))
922 922 if lvals:
923 923 lvals = '%s%s' % (indent,em_normal.join(lvals))
924 924 else:
925 925 lvals = ''
926 926
927 927 level = '%s %s\n' % (link,call)
928 928
929 929 if index is None:
930 930 frames.append(level)
931 931 else:
932 932 frames.append('%s%s' % (level,''.join(
933 933 _format_traceback_lines(lnum,index,lines,Colors,lvals,
934 934 col_scheme))))
935 935
936 936 # Get (safely) a string form of the exception info
937 937 try:
938 938 etype_str,evalue_str = map(str,(etype,evalue))
939 939 except:
940 940 # User exception is improperly defined.
941 941 etype,evalue = str,sys.exc_info()[:2]
942 942 etype_str,evalue_str = map(str,(etype,evalue))
943 943 # ... and format it
944 944 exception = ['%s%s%s: %s' % (Colors.excName, etype_str,
945 945 ColorsNormal, py3compat.cast_unicode(evalue_str))]
946 946 if (not py3compat.PY3) and type(evalue) is types.InstanceType:
947 947 try:
948 948 names = [w for w in dir(evalue) if isinstance(w, py3compat.string_types)]
949 949 except:
950 950 # Every now and then, an object with funny inernals blows up
951 951 # when dir() is called on it. We do the best we can to report
952 952 # the problem and continue
953 953 _m = '%sException reporting error (object with broken dir())%s:'
954 954 exception.append(_m % (Colors.excName,ColorsNormal))
955 955 etype_str,evalue_str = map(str,sys.exc_info()[:2])
956 956 exception.append('%s%s%s: %s' % (Colors.excName,etype_str,
957 957 ColorsNormal, py3compat.cast_unicode(evalue_str)))
958 958 names = []
959 959 for name in names:
960 960 value = text_repr(getattr(evalue, name))
961 961 exception.append('\n%s%s = %s' % (indent, name, value))
962 962
963 963 # vds: >>
964 964 if records:
965 965 filepath, lnum = records[-1][1:3]
966 966 #print "file:", str(file), "linenb", str(lnum) # dbg
967 967 filepath = os.path.abspath(filepath)
968 968 ipinst = get_ipython()
969 969 if ipinst is not None:
970 970 ipinst.hooks.synchronize_with_editor(filepath, lnum, 0)
971 971 # vds: <<
972 972
973 973 # return all our info assembled as a single string
974 974 # return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
975 975 return [head] + frames + [''.join(exception[0])]
976 976
977 977 def debugger(self,force=False):
978 978 """Call up the pdb debugger if desired, always clean up the tb
979 979 reference.
980 980
981 981 Keywords:
982 982
983 983 - force(False): by default, this routine checks the instance call_pdb
984 984 flag and does not actually invoke the debugger if the flag is false.
985 985 The 'force' option forces the debugger to activate even if the flag
986 986 is false.
987 987
988 988 If the call_pdb flag is set, the pdb interactive debugger is
989 989 invoked. In all cases, the self.tb reference to the current traceback
990 990 is deleted to prevent lingering references which hamper memory
991 991 management.
992 992
993 993 Note that each call to pdb() does an 'import readline', so if your app
994 994 requires a special setup for the readline completers, you'll have to
995 995 fix that by hand after invoking the exception handler."""
996 996
997 997 if force or self.call_pdb:
998 998 if self.pdb is None:
999 999 self.pdb = debugger.Pdb(
1000 1000 self.color_scheme_table.active_scheme_name)
1001 1001 # the system displayhook may have changed, restore the original
1002 1002 # for pdb
1003 1003 display_trap = DisplayTrap(hook=sys.__displayhook__)
1004 1004 with display_trap:
1005 1005 self.pdb.reset()
1006 1006 # Find the right frame so we don't pop up inside ipython itself
1007 1007 if hasattr(self,'tb') and self.tb is not None:
1008 1008 etb = self.tb
1009 1009 else:
1010 1010 etb = self.tb = sys.last_traceback
1011 1011 while self.tb is not None and self.tb.tb_next is not None:
1012 1012 self.tb = self.tb.tb_next
1013 1013 if etb and etb.tb_next:
1014 1014 etb = etb.tb_next
1015 1015 self.pdb.botframe = etb.tb_frame
1016 1016 self.pdb.interaction(self.tb.tb_frame, self.tb)
1017 1017
1018 1018 if hasattr(self,'tb'):
1019 1019 del self.tb
1020 1020
1021 1021 def handler(self, info=None):
1022 1022 (etype, evalue, etb) = info or sys.exc_info()
1023 1023 self.tb = etb
1024 1024 ostream = self.ostream
1025 1025 ostream.flush()
1026 1026 ostream.write(self.text(etype, evalue, etb))
1027 1027 ostream.write('\n')
1028 1028 ostream.flush()
1029 1029
1030 1030 # Changed so an instance can just be called as VerboseTB_inst() and print
1031 1031 # out the right info on its own.
1032 1032 def __call__(self, etype=None, evalue=None, etb=None):
1033 1033 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1034 1034 if etb is None:
1035 1035 self.handler()
1036 1036 else:
1037 1037 self.handler((etype, evalue, etb))
1038 1038 try:
1039 1039 self.debugger()
1040 1040 except KeyboardInterrupt:
1041 1041 print("\nKeyboardInterrupt")
1042 1042
1043 1043 #----------------------------------------------------------------------------
1044 1044 class FormattedTB(VerboseTB, ListTB):
1045 1045 """Subclass ListTB but allow calling with a traceback.
1046 1046
1047 1047 It can thus be used as a sys.excepthook for Python > 2.1.
1048 1048
1049 1049 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1050 1050
1051 1051 Allows a tb_offset to be specified. This is useful for situations where
1052 1052 one needs to remove a number of topmost frames from the traceback (such as
1053 1053 occurs with python programs that themselves execute other python code,
1054 1054 like Python shells). """
1055 1055
1056 1056 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1057 1057 ostream=None,
1058 1058 tb_offset=0, long_header=False, include_vars=False,
1059 1059 check_cache=None):
1060 1060
1061 1061 # NEVER change the order of this list. Put new modes at the end:
1062 1062 self.valid_modes = ['Plain','Context','Verbose']
1063 1063 self.verbose_modes = self.valid_modes[1:3]
1064 1064
1065 1065 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1066 1066 ostream=ostream, tb_offset=tb_offset,
1067 1067 long_header=long_header, include_vars=include_vars,
1068 1068 check_cache=check_cache)
1069 1069
1070 1070 # Different types of tracebacks are joined with different separators to
1071 1071 # form a single string. They are taken from this dict
1072 1072 self._join_chars = dict(Plain='', Context='\n', Verbose='\n')
1073 1073 # set_mode also sets the tb_join_char attribute
1074 1074 self.set_mode(mode)
1075 1075
1076 1076 def _extract_tb(self,tb):
1077 1077 if tb:
1078 1078 return traceback.extract_tb(tb)
1079 1079 else:
1080 1080 return None
1081 1081
1082 1082 def structured_traceback(self, etype, value, tb, tb_offset=None, context=5):
1083 1083 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1084 1084 mode = self.mode
1085 1085 if mode in self.verbose_modes:
1086 1086 # Verbose modes need a full traceback
1087 1087 return VerboseTB.structured_traceback(
1088 1088 self, etype, value, tb, tb_offset, context
1089 1089 )
1090 1090 else:
1091 1091 # We must check the source cache because otherwise we can print
1092 1092 # out-of-date source code.
1093 1093 self.check_cache()
1094 1094 # Now we can extract and format the exception
1095 1095 elist = self._extract_tb(tb)
1096 1096 return ListTB.structured_traceback(
1097 1097 self, etype, value, elist, tb_offset, context
1098 1098 )
1099 1099
1100 1100 def stb2text(self, stb):
1101 1101 """Convert a structured traceback (a list) to a string."""
1102 1102 return self.tb_join_char.join(stb)
1103 1103
1104 1104
1105 1105 def set_mode(self,mode=None):
1106 1106 """Switch to the desired mode.
1107 1107
1108 1108 If mode is not specified, cycles through the available modes."""
1109 1109
1110 1110 if not mode:
1111 1111 new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \
1112 1112 len(self.valid_modes)
1113 1113 self.mode = self.valid_modes[new_idx]
1114 1114 elif mode not in self.valid_modes:
1115 1115 raise ValueError('Unrecognized mode in FormattedTB: <'+mode+'>\n'
1116 1116 'Valid modes: '+str(self.valid_modes))
1117 1117 else:
1118 1118 self.mode = mode
1119 1119 # include variable details only in 'Verbose' mode
1120 1120 self.include_vars = (self.mode == self.valid_modes[2])
1121 1121 # Set the join character for generating text tracebacks
1122 1122 self.tb_join_char = self._join_chars[self.mode]
1123 1123
1124 1124 # some convenient shorcuts
1125 1125 def plain(self):
1126 1126 self.set_mode(self.valid_modes[0])
1127 1127
1128 1128 def context(self):
1129 1129 self.set_mode(self.valid_modes[1])
1130 1130
1131 1131 def verbose(self):
1132 1132 self.set_mode(self.valid_modes[2])
1133 1133
1134 1134 #----------------------------------------------------------------------------
1135 1135 class AutoFormattedTB(FormattedTB):
1136 1136 """A traceback printer which can be called on the fly.
1137 1137
1138 1138 It will find out about exceptions by itself.
1139 1139
1140 1140 A brief example::
1141 1141
1142 1142 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1143 1143 try:
1144 1144 ...
1145 1145 except:
1146 1146 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1147 1147 """
1148 1148
1149 1149 def __call__(self,etype=None,evalue=None,etb=None,
1150 1150 out=None,tb_offset=None):
1151 1151 """Print out a formatted exception traceback.
1152 1152
1153 1153 Optional arguments:
1154 1154 - out: an open file-like object to direct output to.
1155 1155
1156 1156 - tb_offset: the number of frames to skip over in the stack, on a
1157 1157 per-call basis (this overrides temporarily the instance's tb_offset
1158 1158 given at initialization time. """
1159 1159
1160 1160
1161 1161 if out is None:
1162 1162 out = self.ostream
1163 1163 out.flush()
1164 1164 out.write(self.text(etype, evalue, etb, tb_offset))
1165 1165 out.write('\n')
1166 1166 out.flush()
1167 1167 # FIXME: we should remove the auto pdb behavior from here and leave
1168 1168 # that to the clients.
1169 1169 try:
1170 1170 self.debugger()
1171 1171 except KeyboardInterrupt:
1172 1172 print("\nKeyboardInterrupt")
1173 1173
1174 1174 def structured_traceback(self, etype=None, value=None, tb=None,
1175 1175 tb_offset=None, context=5):
1176 1176 if etype is None:
1177 1177 etype,value,tb = sys.exc_info()
1178 1178 self.tb = tb
1179 1179 return FormattedTB.structured_traceback(
1180 1180 self, etype, value, tb, tb_offset, context)
1181 1181
1182 1182 #---------------------------------------------------------------------------
1183 1183
1184 1184 # A simple class to preserve Nathan's original functionality.
1185 1185 class ColorTB(FormattedTB):
1186 1186 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1187 1187 def __init__(self,color_scheme='Linux',call_pdb=0):
1188 1188 FormattedTB.__init__(self,color_scheme=color_scheme,
1189 1189 call_pdb=call_pdb)
1190 1190
1191 1191
1192 1192 class SyntaxTB(ListTB):
1193 1193 """Extension which holds some state: the last exception value"""
1194 1194
1195 1195 def __init__(self,color_scheme = 'NoColor'):
1196 1196 ListTB.__init__(self,color_scheme)
1197 1197 self.last_syntax_error = None
1198 1198
1199 1199 def __call__(self, etype, value, elist):
1200 1200 self.last_syntax_error = value
1201 1201 ListTB.__call__(self,etype,value,elist)
1202 1202
1203 1203 def structured_traceback(self, etype, value, elist, tb_offset=None,
1204 1204 context=5):
1205 1205 # If the source file has been edited, the line in the syntax error can
1206 1206 # be wrong (retrieved from an outdated cache). This replaces it with
1207 1207 # the current value.
1208 1208 if isinstance(value, SyntaxError) \
1209 1209 and isinstance(value.filename, py3compat.string_types) \
1210 1210 and isinstance(value.lineno, int):
1211 1211 linecache.checkcache(value.filename)
1212 1212 newtext = ulinecache.getline(value.filename, value.lineno)
1213 1213 if newtext:
1214 1214 value.text = newtext
1215 1215 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1216 1216 tb_offset=tb_offset, context=context)
1217 1217
1218 1218 def clear_err_state(self):
1219 1219 """Return the current error state and clear it"""
1220 1220 e = self.last_syntax_error
1221 1221 self.last_syntax_error = None
1222 1222 return e
1223 1223
1224 1224 def stb2text(self, stb):
1225 1225 """Convert a structured traceback (a list) to a string."""
1226 1226 return ''.join(stb)
1227 1227
1228 1228
1229 1229 #----------------------------------------------------------------------------
1230 1230 # module testing (minimal)
1231 1231 if __name__ == "__main__":
1232 1232 def spam(c, d_e):
1233 1233 (d, e) = d_e
1234 1234 x = c + d
1235 1235 y = c * d
1236 1236 foo(x, y)
1237 1237
1238 1238 def foo(a, b, bar=1):
1239 1239 eggs(a, b + bar)
1240 1240
1241 1241 def eggs(f, g, z=globals()):
1242 1242 h = f + g
1243 1243 i = f - g
1244 1244 return h / i
1245 1245
1246 1246 print('')
1247 1247 print('*** Before ***')
1248 1248 try:
1249 1249 print(spam(1, (2, 3)))
1250 1250 except:
1251 1251 traceback.print_exc()
1252 1252 print('')
1253 1253
1254 1254 handler = ColorTB()
1255 1255 print('*** ColorTB ***')
1256 1256 try:
1257 1257 print(spam(1, (2, 3)))
1258 1258 except:
1259 1259 handler(*sys.exc_info())
1260 1260 print('')
1261 1261
1262 1262 handler = VerboseTB()
1263 1263 print('*** VerboseTB ***')
1264 1264 try:
1265 1265 print(spam(1, (2, 3)))
1266 1266 except:
1267 1267 handler(*sys.exc_info())
1268 1268 print('')
1269 1269
@@ -1,70 +1,70 b''
1 """Publishing
1 """Publishing native (typically pickled) objects.
2 2 """
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2012 The IPython Development Team
6 6 #
7 7 # Distributed under the terms of the BSD License. The full license is in
8 8 # the file COPYING, distributed as part of this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 from IPython.config import Configurable
16 16 from IPython.kernel.inprocess.socket import SocketABC
17 17 from IPython.utils.jsonutil import json_clean
18 18 from IPython.utils.traitlets import Instance, Dict, CBytes
19 19 from IPython.kernel.zmq.serialize import serialize_object
20 20 from IPython.kernel.zmq.session import Session, extract_header
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Code
24 24 #-----------------------------------------------------------------------------
25 25
26 26
27 27 class ZMQDataPublisher(Configurable):
28 28
29 29 topic = topic = CBytes(b'datapub')
30 30 session = Instance(Session)
31 31 pub_socket = Instance(SocketABC)
32 32 parent_header = Dict({})
33 33
34 34 def set_parent(self, parent):
35 35 """Set the parent for outbound messages."""
36 36 self.parent_header = extract_header(parent)
37 37
38 38 def publish_data(self, data):
39 39 """publish a data_message on the IOPub channel
40 40
41 41 Parameters
42 42 ----------
43 43
44 44 data : dict
45 45 The data to be published. Think of it as a namespace.
46 46 """
47 47 session = self.session
48 48 buffers = serialize_object(data,
49 49 buffer_threshold=session.buffer_threshold,
50 50 item_threshold=session.item_threshold,
51 51 )
52 52 content = json_clean(dict(keys=data.keys()))
53 53 session.send(self.pub_socket, 'data_message', content=content,
54 54 parent=self.parent_header,
55 55 buffers=buffers,
56 56 ident=self.topic,
57 57 )
58 58
59 59
60 60 def publish_data(data):
61 61 """publish a data_message on the IOPub channel
62 62
63 63 Parameters
64 64 ----------
65 65
66 66 data : dict
67 67 The data to be published. Think of it as a namespace.
68 68 """
69 69 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
70 70 ZMQInteractiveShell.instance().data_pub.publish_data(data)
@@ -1,65 +1,67 b''
1 """Replacements for sys.displayhook that publish over ZMQ.
2 """
1 3 import sys
2 4
3 5 from IPython.core.displayhook import DisplayHook
4 6 from IPython.kernel.inprocess.socket import SocketABC
5 7 from IPython.utils.jsonutil import encode_images
6 8 from IPython.utils.py3compat import builtin_mod
7 9 from IPython.utils.traitlets import Instance, Dict
8 10 from .session import extract_header, Session
9 11
10 12 class ZMQDisplayHook(object):
11 13 """A simple displayhook that publishes the object's repr over a ZeroMQ
12 14 socket."""
13 15 topic=b'pyout'
14 16
15 17 def __init__(self, session, pub_socket):
16 18 self.session = session
17 19 self.pub_socket = pub_socket
18 20 self.parent_header = {}
19 21
20 22 def __call__(self, obj):
21 23 if obj is None:
22 24 return
23 25
24 26 builtin_mod._ = obj
25 27 sys.stdout.flush()
26 28 sys.stderr.flush()
27 29 msg = self.session.send(self.pub_socket, u'pyout', {u'data':repr(obj)},
28 30 parent=self.parent_header, ident=self.topic)
29 31
30 32 def set_parent(self, parent):
31 33 self.parent_header = extract_header(parent)
32 34
33 35
34 36 class ZMQShellDisplayHook(DisplayHook):
35 37 """A displayhook subclass that publishes data using ZeroMQ. This is intended
36 38 to work with an InteractiveShell instance. It sends a dict of different
37 39 representations of the object."""
38 40 topic=None
39 41
40 42 session = Instance(Session)
41 43 pub_socket = Instance(SocketABC)
42 44 parent_header = Dict({})
43 45
44 46 def set_parent(self, parent):
45 47 """Set the parent for outbound messages."""
46 48 self.parent_header = extract_header(parent)
47 49
48 50 def start_displayhook(self):
49 51 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
50 52
51 53 def write_output_prompt(self):
52 54 """Write the output prompt."""
53 55 self.msg['content']['execution_count'] = self.prompt_count
54 56
55 57 def write_format_data(self, format_dict, md_dict=None):
56 58 self.msg['content']['data'] = encode_images(format_dict)
57 59 self.msg['content']['metadata'] = md_dict
58 60
59 61 def finish_displayhook(self):
60 62 """Finish up all displayhook activities."""
61 63 sys.stdout.flush()
62 64 sys.stderr.flush()
63 65 self.session.send(self.pub_socket, self.msg, ident=self.topic)
64 66 self.msg = None
65 67
@@ -1,370 +1,370 b''
1 """some generic utilities for dealing with classes, urls, and serialization
1 """Some generic utilities for dealing with classes, urls, and serialization.
2 2
3 3 Authors:
4 4
5 5 * Min RK
6 6 """
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2010-2011 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 # Standard library imports.
19 19 import logging
20 20 import os
21 21 import re
22 22 import stat
23 23 import socket
24 24 import sys
25 25 from signal import signal, SIGINT, SIGABRT, SIGTERM
26 26 try:
27 27 from signal import SIGKILL
28 28 except ImportError:
29 29 SIGKILL=None
30 30
31 31 try:
32 32 import cPickle
33 33 pickle = cPickle
34 34 except:
35 35 cPickle = None
36 36 import pickle
37 37
38 38 # System library imports
39 39 import zmq
40 40 from zmq.log import handlers
41 41
42 42 from IPython.external.decorator import decorator
43 43
44 44 # IPython imports
45 45 from IPython.config.application import Application
46 46 from IPython.utils.localinterfaces import localhost, is_public_ip, public_ips
47 47 from IPython.utils.py3compat import string_types, iteritems, itervalues
48 48 from IPython.kernel.zmq.log import EnginePUBHandler
49 49 from IPython.kernel.zmq.serialize import (
50 50 unserialize_object, serialize_object, pack_apply_message, unpack_apply_message
51 51 )
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Classes
55 55 #-----------------------------------------------------------------------------
56 56
57 57 class Namespace(dict):
58 58 """Subclass of dict for attribute access to keys."""
59 59
60 60 def __getattr__(self, key):
61 61 """getattr aliased to getitem"""
62 62 if key in self:
63 63 return self[key]
64 64 else:
65 65 raise NameError(key)
66 66
67 67 def __setattr__(self, key, value):
68 68 """setattr aliased to setitem, with strict"""
69 69 if hasattr(dict, key):
70 70 raise KeyError("Cannot override dict keys %r"%key)
71 71 self[key] = value
72 72
73 73
74 74 class ReverseDict(dict):
75 75 """simple double-keyed subset of dict methods."""
76 76
77 77 def __init__(self, *args, **kwargs):
78 78 dict.__init__(self, *args, **kwargs)
79 79 self._reverse = dict()
80 80 for key, value in iteritems(self):
81 81 self._reverse[value] = key
82 82
83 83 def __getitem__(self, key):
84 84 try:
85 85 return dict.__getitem__(self, key)
86 86 except KeyError:
87 87 return self._reverse[key]
88 88
89 89 def __setitem__(self, key, value):
90 90 if key in self._reverse:
91 91 raise KeyError("Can't have key %r on both sides!"%key)
92 92 dict.__setitem__(self, key, value)
93 93 self._reverse[value] = key
94 94
95 95 def pop(self, key):
96 96 value = dict.pop(self, key)
97 97 self._reverse.pop(value)
98 98 return value
99 99
100 100 def get(self, key, default=None):
101 101 try:
102 102 return self[key]
103 103 except KeyError:
104 104 return default
105 105
106 106 #-----------------------------------------------------------------------------
107 107 # Functions
108 108 #-----------------------------------------------------------------------------
109 109
110 110 @decorator
111 111 def log_errors(f, self, *args, **kwargs):
112 112 """decorator to log unhandled exceptions raised in a method.
113 113
114 114 For use wrapping on_recv callbacks, so that exceptions
115 115 do not cause the stream to be closed.
116 116 """
117 117 try:
118 118 return f(self, *args, **kwargs)
119 119 except Exception:
120 120 self.log.error("Uncaught exception in %r" % f, exc_info=True)
121 121
122 122
123 123 def is_url(url):
124 124 """boolean check for whether a string is a zmq url"""
125 125 if '://' not in url:
126 126 return False
127 127 proto, addr = url.split('://', 1)
128 128 if proto.lower() not in ['tcp','pgm','epgm','ipc','inproc']:
129 129 return False
130 130 return True
131 131
132 132 def validate_url(url):
133 133 """validate a url for zeromq"""
134 134 if not isinstance(url, string_types):
135 135 raise TypeError("url must be a string, not %r"%type(url))
136 136 url = url.lower()
137 137
138 138 proto_addr = url.split('://')
139 139 assert len(proto_addr) == 2, 'Invalid url: %r'%url
140 140 proto, addr = proto_addr
141 141 assert proto in ['tcp','pgm','epgm','ipc','inproc'], "Invalid protocol: %r"%proto
142 142
143 143 # domain pattern adapted from http://www.regexlib.com/REDetails.aspx?regexp_id=391
144 144 # author: Remi Sabourin
145 145 pat = re.compile(r'^([\w\d]([\w\d\-]{0,61}[\w\d])?\.)*[\w\d]([\w\d\-]{0,61}[\w\d])?$')
146 146
147 147 if proto == 'tcp':
148 148 lis = addr.split(':')
149 149 assert len(lis) == 2, 'Invalid url: %r'%url
150 150 addr,s_port = lis
151 151 try:
152 152 port = int(s_port)
153 153 except ValueError:
154 154 raise AssertionError("Invalid port %r in url: %r"%(port, url))
155 155
156 156 assert addr == '*' or pat.match(addr) is not None, 'Invalid url: %r'%url
157 157
158 158 else:
159 159 # only validate tcp urls currently
160 160 pass
161 161
162 162 return True
163 163
164 164
165 165 def validate_url_container(container):
166 166 """validate a potentially nested collection of urls."""
167 167 if isinstance(container, string_types):
168 168 url = container
169 169 return validate_url(url)
170 170 elif isinstance(container, dict):
171 171 container = itervalues(container)
172 172
173 173 for element in container:
174 174 validate_url_container(element)
175 175
176 176
177 177 def split_url(url):
178 178 """split a zmq url (tcp://ip:port) into ('tcp','ip','port')."""
179 179 proto_addr = url.split('://')
180 180 assert len(proto_addr) == 2, 'Invalid url: %r'%url
181 181 proto, addr = proto_addr
182 182 lis = addr.split(':')
183 183 assert len(lis) == 2, 'Invalid url: %r'%url
184 184 addr,s_port = lis
185 185 return proto,addr,s_port
186 186
187 187 def disambiguate_ip_address(ip, location=None):
188 188 """turn multi-ip interfaces '0.0.0.0' and '*' into connectable
189 189 ones, based on the location (default interpretation of location is localhost)."""
190 190 if ip in ('0.0.0.0', '*'):
191 191 if location is None or is_public_ip(location) or not public_ips():
192 192 # If location is unspecified or cannot be determined, assume local
193 193 ip = localhost()
194 194 elif location:
195 195 return location
196 196 return ip
197 197
198 198 def disambiguate_url(url, location=None):
199 199 """turn multi-ip interfaces '0.0.0.0' and '*' into connectable
200 200 ones, based on the location (default interpretation is localhost).
201 201
202 202 This is for zeromq urls, such as ``tcp://*:10101``.
203 203 """
204 204 try:
205 205 proto,ip,port = split_url(url)
206 206 except AssertionError:
207 207 # probably not tcp url; could be ipc, etc.
208 208 return url
209 209
210 210 ip = disambiguate_ip_address(ip,location)
211 211
212 212 return "%s://%s:%s"%(proto,ip,port)
213 213
214 214
215 215 #--------------------------------------------------------------------------
216 216 # helpers for implementing old MEC API via view.apply
217 217 #--------------------------------------------------------------------------
218 218
219 219 def interactive(f):
220 220 """decorator for making functions appear as interactively defined.
221 221 This results in the function being linked to the user_ns as globals()
222 222 instead of the module globals().
223 223 """
224 224 f.__module__ = '__main__'
225 225 return f
226 226
227 227 @interactive
228 228 def _push(**ns):
229 229 """helper method for implementing `client.push` via `client.apply`"""
230 230 user_ns = globals()
231 231 tmp = '_IP_PUSH_TMP_'
232 232 while tmp in user_ns:
233 233 tmp = tmp + '_'
234 234 try:
235 235 for name, value in ns.items():
236 236 user_ns[tmp] = value
237 237 exec("%s = %s" % (name, tmp), user_ns)
238 238 finally:
239 239 user_ns.pop(tmp, None)
240 240
241 241 @interactive
242 242 def _pull(keys):
243 243 """helper method for implementing `client.pull` via `client.apply`"""
244 244 if isinstance(keys, (list,tuple, set)):
245 245 return [eval(key, globals()) for key in keys]
246 246 else:
247 247 return eval(keys, globals())
248 248
249 249 @interactive
250 250 def _execute(code):
251 251 """helper method for implementing `client.execute` via `client.apply`"""
252 252 exec(code, globals())
253 253
254 254 #--------------------------------------------------------------------------
255 255 # extra process management utilities
256 256 #--------------------------------------------------------------------------
257 257
258 258 _random_ports = set()
259 259
260 260 def select_random_ports(n):
261 261 """Selects and return n random ports that are available."""
262 262 ports = []
263 263 for i in range(n):
264 264 sock = socket.socket()
265 265 sock.bind(('', 0))
266 266 while sock.getsockname()[1] in _random_ports:
267 267 sock.close()
268 268 sock = socket.socket()
269 269 sock.bind(('', 0))
270 270 ports.append(sock)
271 271 for i, sock in enumerate(ports):
272 272 port = sock.getsockname()[1]
273 273 sock.close()
274 274 ports[i] = port
275 275 _random_ports.add(port)
276 276 return ports
277 277
278 278 def signal_children(children):
279 279 """Relay interupt/term signals to children, for more solid process cleanup."""
280 280 def terminate_children(sig, frame):
281 281 log = Application.instance().log
282 282 log.critical("Got signal %i, terminating children..."%sig)
283 283 for child in children:
284 284 child.terminate()
285 285
286 286 sys.exit(sig != SIGINT)
287 287 # sys.exit(sig)
288 288 for sig in (SIGINT, SIGABRT, SIGTERM):
289 289 signal(sig, terminate_children)
290 290
291 291 def generate_exec_key(keyfile):
292 292 import uuid
293 293 newkey = str(uuid.uuid4())
294 294 with open(keyfile, 'w') as f:
295 295 # f.write('ipython-key ')
296 296 f.write(newkey+'\n')
297 297 # set user-only RW permissions (0600)
298 298 # this will have no effect on Windows
299 299 os.chmod(keyfile, stat.S_IRUSR|stat.S_IWUSR)
300 300
301 301
302 302 def integer_loglevel(loglevel):
303 303 try:
304 304 loglevel = int(loglevel)
305 305 except ValueError:
306 306 if isinstance(loglevel, str):
307 307 loglevel = getattr(logging, loglevel)
308 308 return loglevel
309 309
310 310 def connect_logger(logname, context, iface, root="ip", loglevel=logging.DEBUG):
311 311 logger = logging.getLogger(logname)
312 312 if any([isinstance(h, handlers.PUBHandler) for h in logger.handlers]):
313 313 # don't add a second PUBHandler
314 314 return
315 315 loglevel = integer_loglevel(loglevel)
316 316 lsock = context.socket(zmq.PUB)
317 317 lsock.connect(iface)
318 318 handler = handlers.PUBHandler(lsock)
319 319 handler.setLevel(loglevel)
320 320 handler.root_topic = root
321 321 logger.addHandler(handler)
322 322 logger.setLevel(loglevel)
323 323
324 324 def connect_engine_logger(context, iface, engine, loglevel=logging.DEBUG):
325 325 logger = logging.getLogger()
326 326 if any([isinstance(h, handlers.PUBHandler) for h in logger.handlers]):
327 327 # don't add a second PUBHandler
328 328 return
329 329 loglevel = integer_loglevel(loglevel)
330 330 lsock = context.socket(zmq.PUB)
331 331 lsock.connect(iface)
332 332 handler = EnginePUBHandler(engine, lsock)
333 333 handler.setLevel(loglevel)
334 334 logger.addHandler(handler)
335 335 logger.setLevel(loglevel)
336 336 return logger
337 337
338 338 def local_logger(logname, loglevel=logging.DEBUG):
339 339 loglevel = integer_loglevel(loglevel)
340 340 logger = logging.getLogger(logname)
341 341 if any([isinstance(h, logging.StreamHandler) for h in logger.handlers]):
342 342 # don't add a second StreamHandler
343 343 return
344 344 handler = logging.StreamHandler()
345 345 handler.setLevel(loglevel)
346 346 formatter = logging.Formatter("%(asctime)s.%(msecs).03d [%(name)s] %(message)s",
347 347 datefmt="%Y-%m-%d %H:%M:%S")
348 348 handler.setFormatter(formatter)
349 349
350 350 logger.addHandler(handler)
351 351 logger.setLevel(loglevel)
352 352 return logger
353 353
354 354 def set_hwm(sock, hwm=0):
355 355 """set zmq High Water Mark on a socket
356 356
357 357 in a way that always works for various pyzmq / libzmq versions.
358 358 """
359 359 import zmq
360 360
361 361 for key in ('HWM', 'SNDHWM', 'RCVHWM'):
362 362 opt = getattr(zmq, key, None)
363 363 if opt is None:
364 364 continue
365 365 try:
366 366 sock.setsockopt(opt, hwm)
367 367 except zmq.ZMQError:
368 368 pass
369 369
370 370 No newline at end of file
@@ -1,371 +1,371 b''
1 """a navigable completer for the qtconsole"""
1 """A navigable completer for the qtconsole"""
2 2 # coding : utf-8
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2012, IPython Development Team.$
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.$
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 # System library imports
12 12 import IPython.utils.text as text
13 13
14 14 from IPython.external.qt import QtCore, QtGui
15 15
16 16 #--------------------------------------------------------------------------
17 17 # Return an HTML table with selected item in a special class
18 18 #--------------------------------------------------------------------------
19 19 def html_tableify(item_matrix, select=None, header=None , footer=None) :
20 20 """ returnr a string for an html table"""
21 21 if not item_matrix :
22 22 return ''
23 23 html_cols = []
24 24 tds = lambda text : u'<td>'+text+u' </td>'
25 25 trs = lambda text : u'<tr>'+text+u'</tr>'
26 26 tds_items = [list(map(tds, row)) for row in item_matrix]
27 27 if select :
28 28 row, col = select
29 29 tds_items[row][col] = u'<td class="inverted">'\
30 30 +item_matrix[row][col]\
31 31 +u' </td>'
32 32 #select the right item
33 33 html_cols = map(trs, (u''.join(row) for row in tds_items))
34 34 head = ''
35 35 foot = ''
36 36 if header :
37 37 head = (u'<tr>'\
38 38 +''.join((u'<td>'+header+u'</td>')*len(item_matrix[0]))\
39 39 +'</tr>')
40 40
41 41 if footer :
42 42 foot = (u'<tr>'\
43 43 +''.join((u'<td>'+footer+u'</td>')*len(item_matrix[0]))\
44 44 +'</tr>')
45 45 html = (u'<table class="completion" style="white-space:pre">'+head+(u''.join(html_cols))+foot+u'</table>')
46 46 return html
47 47
48 48 class SlidingInterval(object):
49 49 """a bound interval that follows a cursor
50 50
51 51 internally used to scoll the completion view when the cursor
52 52 try to go beyond the edges, and show '...' when rows are hidden
53 53 """
54 54
55 55 _min = 0
56 56 _max = 1
57 57 _current = 0
58 58 def __init__(self, maximum=1, width=6, minimum=0, sticky_lenght=1):
59 59 """Create a new bounded interval
60 60
61 61 any value return by this will be bound between maximum and
62 62 minimum. usual width will be 'width', and sticky_length
63 63 set when the return interval should expand to max and min
64 64 """
65 65 self._min = minimum
66 66 self._max = maximum
67 67 self._start = 0
68 68 self._width = width
69 69 self._stop = self._start+self._width+1
70 70 self._sticky_lenght = sticky_lenght
71 71
72 72 @property
73 73 def current(self):
74 74 """current cursor position"""
75 75 return self._current
76 76
77 77 @current.setter
78 78 def current(self, value):
79 79 """set current cursor position"""
80 80 current = min(max(self._min, value), self._max)
81 81
82 82 self._current = current
83 83
84 84 if current > self._stop :
85 85 self._stop = current
86 86 self._start = current-self._width
87 87 elif current < self._start :
88 88 self._start = current
89 89 self._stop = current + self._width
90 90
91 91 if abs(self._start - self._min) <= self._sticky_lenght :
92 92 self._start = self._min
93 93
94 94 if abs(self._stop - self._max) <= self._sticky_lenght :
95 95 self._stop = self._max
96 96
97 97 @property
98 98 def start(self):
99 99 """begiiing of interval to show"""
100 100 return self._start
101 101
102 102 @property
103 103 def stop(self):
104 104 """end of interval to show"""
105 105 return self._stop
106 106
107 107 @property
108 108 def width(self):
109 109 return self._stop - self._start
110 110
111 111 @property
112 112 def nth(self):
113 113 return self.current - self.start
114 114
115 115 class CompletionHtml(QtGui.QWidget):
116 116 """ A widget for tab completion, navigable by arrow keys """
117 117
118 118 #--------------------------------------------------------------------------
119 119 # 'QObject' interface
120 120 #--------------------------------------------------------------------------
121 121
122 122 _items = ()
123 123 _index = (0, 0)
124 124 _consecutive_tab = 0
125 125 _size = (1, 1)
126 126 _old_cursor = None
127 127 _start_position = 0
128 128 _slice_start = 0
129 129 _slice_len = 4
130 130
131 131 def __init__(self, console_widget):
132 132 """ Create a completion widget that is attached to the specified Qt
133 133 text edit widget.
134 134 """
135 135 assert isinstance(console_widget._control, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
136 136 super(CompletionHtml, self).__init__()
137 137
138 138 self._text_edit = console_widget._control
139 139 self._console_widget = console_widget
140 140 self._text_edit.installEventFilter(self)
141 141 self._sliding_interval = None
142 142 self._justified_items = None
143 143
144 144 # Ensure that the text edit keeps focus when widget is displayed.
145 145 self.setFocusProxy(self._text_edit)
146 146
147 147
148 148 def eventFilter(self, obj, event):
149 149 """ Reimplemented to handle keyboard input and to auto-hide when the
150 150 text edit loses focus.
151 151 """
152 152 if obj == self._text_edit:
153 153 etype = event.type()
154 154 if etype == QtCore.QEvent.KeyPress:
155 155 key = event.key()
156 156 if self._consecutive_tab == 0 and key in (QtCore.Qt.Key_Tab,):
157 157 return False
158 158 elif self._consecutive_tab == 1 and key in (QtCore.Qt.Key_Tab,):
159 159 # ok , called twice, we grab focus, and show the cursor
160 160 self._consecutive_tab = self._consecutive_tab+1
161 161 self._update_list()
162 162 return True
163 163 elif self._consecutive_tab == 2:
164 164 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
165 165 self._complete_current()
166 166 return True
167 167 if key in (QtCore.Qt.Key_Tab,):
168 168 self.select_right()
169 169 self._update_list()
170 170 return True
171 171 elif key in ( QtCore.Qt.Key_Down,):
172 172 self.select_down()
173 173 self._update_list()
174 174 return True
175 175 elif key in (QtCore.Qt.Key_Right,):
176 176 self.select_right()
177 177 self._update_list()
178 178 return True
179 179 elif key in ( QtCore.Qt.Key_Up,):
180 180 self.select_up()
181 181 self._update_list()
182 182 return True
183 183 elif key in ( QtCore.Qt.Key_Left,):
184 184 self.select_left()
185 185 self._update_list()
186 186 return True
187 187 elif key in ( QtCore.Qt.Key_Escape,):
188 188 self.cancel_completion()
189 189 return True
190 190 else :
191 191 self.cancel_completion()
192 192 else:
193 193 self.cancel_completion()
194 194
195 195 elif etype == QtCore.QEvent.FocusOut:
196 196 self.cancel_completion()
197 197
198 198 return super(CompletionHtml, self).eventFilter(obj, event)
199 199
200 200 #--------------------------------------------------------------------------
201 201 # 'CompletionHtml' interface
202 202 #--------------------------------------------------------------------------
203 203 def cancel_completion(self):
204 204 """Cancel the completion
205 205
206 206 should be called when the completer have to be dismissed
207 207
208 208 This reset internal variable, clearing the temporary buffer
209 209 of the console where the completion are shown.
210 210 """
211 211 self._consecutive_tab = 0
212 212 self._slice_start = 0
213 213 self._console_widget._clear_temporary_buffer()
214 214 self._index = (0, 0)
215 215 if(self._sliding_interval):
216 216 self._sliding_interval = None
217 217
218 218 #
219 219 # ... 2 4 4 4 4 4 4 4 4 4 4 4 4
220 220 # 2 2 4 4 4 4 4 4 4 4 4 4 4 4
221 221 #
222 222 #2 2 x x x x x x x x x x x 5 5
223 223 #6 6 x x x x x x x x x x x 5 5
224 224 #6 6 x x x x x x x x x x ? 5 5
225 225 #6 6 x x x x x x x x x x ? 1 1
226 226 #
227 227 #3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 ...
228 228 #3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 ...
229 229 def _select_index(self, row, col):
230 230 """Change the selection index, and make sure it stays in the right range
231 231
232 232 A little more complicated than just dooing modulo the number of row columns
233 233 to be sure to cycle through all element.
234 234
235 235 horizontaly, the element are maped like this :
236 236 to r <-- a b c d e f --> to g
237 237 to f <-- g h i j k l --> to m
238 238 to l <-- m n o p q r --> to a
239 239
240 240 and vertically
241 241 a d g j m p
242 242 b e h k n q
243 243 c f i l o r
244 244 """
245 245
246 246 nr, nc = self._size
247 247 nr = nr-1
248 248 nc = nc-1
249 249
250 250 # case 1
251 251 if (row > nr and col >= nc) or (row >= nr and col > nc):
252 252 self._select_index(0, 0)
253 253 # case 2
254 254 elif (row <= 0 and col < 0) or (row < 0 and col <= 0):
255 255 self._select_index(nr, nc)
256 256 # case 3
257 257 elif row > nr :
258 258 self._select_index(0, col+1)
259 259 # case 4
260 260 elif row < 0 :
261 261 self._select_index(nr, col-1)
262 262 # case 5
263 263 elif col > nc :
264 264 self._select_index(row+1, 0)
265 265 # case 6
266 266 elif col < 0 :
267 267 self._select_index(row-1, nc)
268 268 elif 0 <= row and row <= nr and 0 <= col and col <= nc :
269 269 self._index = (row, col)
270 270 else :
271 271 raise NotImplementedError("you'r trying to go where no completion\
272 272 have gone before : %d:%d (%d:%d)"%(row, col, nr, nc) )
273 273
274 274
275 275 @property
276 276 def _slice_end(self):
277 277 end = self._slice_start+self._slice_len
278 278 if end > len(self._items) :
279 279 return None
280 280 return end
281 281
282 282 def select_up(self):
283 283 """move cursor up"""
284 284 r, c = self._index
285 285 self._select_index(r-1, c)
286 286
287 287 def select_down(self):
288 288 """move cursor down"""
289 289 r, c = self._index
290 290 self._select_index(r+1, c)
291 291
292 292 def select_left(self):
293 293 """move cursor left"""
294 294 r, c = self._index
295 295 self._select_index(r, c-1)
296 296
297 297 def select_right(self):
298 298 """move cursor right"""
299 299 r, c = self._index
300 300 self._select_index(r, c+1)
301 301
302 302 def show_items(self, cursor, items):
303 303 """ Shows the completion widget with 'items' at the position specified
304 304 by 'cursor'.
305 305 """
306 306 if not items :
307 307 return
308 308 self._start_position = cursor.position()
309 309 self._consecutive_tab = 1
310 310 items_m, ci = text.compute_item_matrix(items, empty=' ')
311 311 self._sliding_interval = SlidingInterval(len(items_m)-1)
312 312
313 313 self._items = items_m
314 314 self._size = (ci['rows_numbers'], ci['columns_numbers'])
315 315 self._old_cursor = cursor
316 316 self._index = (0, 0)
317 317 sjoin = lambda x : [ y.ljust(w, ' ') for y, w in zip(x, ci['columns_width'])]
318 318 self._justified_items = list(map(sjoin, items_m))
319 319 self._update_list(hilight=False)
320 320
321 321
322 322
323 323
324 324 def _update_list(self, hilight=True):
325 325 """ update the list of completion and hilight the currently selected completion """
326 326 self._sliding_interval.current = self._index[0]
327 327 head = None
328 328 foot = None
329 329 if self._sliding_interval.start > 0 :
330 330 head = '...'
331 331
332 332 if self._sliding_interval.stop < self._sliding_interval._max:
333 333 foot = '...'
334 334 items_m = self._justified_items[\
335 335 self._sliding_interval.start:\
336 336 self._sliding_interval.stop+1\
337 337 ]
338 338
339 339 self._console_widget._clear_temporary_buffer()
340 340 if(hilight):
341 341 sel = (self._sliding_interval.nth, self._index[1])
342 342 else :
343 343 sel = None
344 344
345 345 strng = html_tableify(items_m, select=sel, header=head, footer=foot)
346 346 self._console_widget._fill_temporary_buffer(self._old_cursor, strng, html=True)
347 347
348 348 #--------------------------------------------------------------------------
349 349 # Protected interface
350 350 #--------------------------------------------------------------------------
351 351
352 352 def _complete_current(self):
353 353 """ Perform the completion with the currently selected item.
354 354 """
355 355 i = self._index
356 356 item = self._items[i[0]][i[1]]
357 357 item = item.strip()
358 358 if item :
359 359 self._current_text_cursor().insertText(item)
360 360 self.cancel_completion()
361 361
362 362 def _current_text_cursor(self):
363 363 """ Returns a cursor with text between the start position and the
364 364 current position selected.
365 365 """
366 366 cursor = self._text_edit.textCursor()
367 367 if cursor.position() >= self._start_position:
368 368 cursor.setPosition(self._start_position,
369 369 QtGui.QTextCursor.KeepAnchor)
370 370 return cursor
371 371
@@ -1,62 +1,62 b''
1 """a simple completer for the qtconsole"""
1 """A simple completer for the qtconsole"""
2 2 #-----------------------------------------------------------------------------
3 3 # Copyright (c) 2012, IPython Development Team.$
4 4 #
5 5 # Distributed under the terms of the Modified BSD License.$
6 6 #
7 7 # The full license is in the file COPYING.txt, distributed with this software.
8 8 #-------------------------------------------------------------------
9 9
10 10 # System library imports
11 11 from IPython.external.qt import QtCore, QtGui
12 12 import IPython.utils.text as text
13 13
14 14
15 15 class CompletionPlain(QtGui.QWidget):
16 16 """ A widget for tab completion, navigable by arrow keys """
17 17
18 18 #--------------------------------------------------------------------------
19 19 # 'QObject' interface
20 20 #--------------------------------------------------------------------------
21 21
22 22 def __init__(self, console_widget):
23 23 """ Create a completion widget that is attached to the specified Qt
24 24 text edit widget.
25 25 """
26 26 assert isinstance(console_widget._control, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
27 27 super(CompletionPlain, self).__init__()
28 28
29 29 self._text_edit = console_widget._control
30 30 self._console_widget = console_widget
31 31
32 32 self._text_edit.installEventFilter(self)
33 33
34 34 def eventFilter(self, obj, event):
35 35 """ Reimplemented to handle keyboard input and to auto-hide when the
36 36 text edit loses focus.
37 37 """
38 38 if obj == self._text_edit:
39 39 etype = event.type()
40 40
41 41 if etype in( QtCore.QEvent.KeyPress, QtCore.QEvent.FocusOut ):
42 42 self.cancel_completion()
43 43
44 44 return super(CompletionPlain, self).eventFilter(obj, event)
45 45
46 46 #--------------------------------------------------------------------------
47 47 # 'CompletionPlain' interface
48 48 #--------------------------------------------------------------------------
49 49 def cancel_completion(self):
50 50 """Cancel the completion, reseting internal variable, clearing buffer """
51 51 self._console_widget._clear_temporary_buffer()
52 52
53 53
54 54 def show_items(self, cursor, items):
55 55 """ Shows the completion widget with 'items' at the position specified
56 56 by 'cursor'.
57 57 """
58 58 if not items :
59 59 return
60 60 self.cancel_completion()
61 61 strng = text.columnize(items)
62 62 self._console_widget._fill_temporary_buffer(cursor, strng, html=False)
@@ -1,138 +1,139 b''
1 """A dropdown completer widget for the qtconsole."""
1 2 # System library imports
2 3 from IPython.external.qt import QtCore, QtGui
3 4
4 5
5 6 class CompletionWidget(QtGui.QListWidget):
6 7 """ A widget for GUI tab completion.
7 8 """
8 9
9 10 #--------------------------------------------------------------------------
10 11 # 'QObject' interface
11 12 #--------------------------------------------------------------------------
12 13
13 14 def __init__(self, console_widget):
14 15 """ Create a completion widget that is attached to the specified Qt
15 16 text edit widget.
16 17 """
17 18 text_edit = console_widget._control
18 19 assert isinstance(text_edit, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
19 20 super(CompletionWidget, self).__init__()
20 21
21 22 self._text_edit = text_edit
22 23
23 24 self.setAttribute(QtCore.Qt.WA_StaticContents)
24 25 self.setWindowFlags(QtCore.Qt.ToolTip | QtCore.Qt.WindowStaysOnTopHint)
25 26
26 27 # Ensure that the text edit keeps focus when widget is displayed.
27 28 self.setFocusProxy(self._text_edit)
28 29
29 30 self.setFrameShadow(QtGui.QFrame.Plain)
30 31 self.setFrameShape(QtGui.QFrame.StyledPanel)
31 32
32 33 self.itemActivated.connect(self._complete_current)
33 34
34 35 def eventFilter(self, obj, event):
35 36 """ Reimplemented to handle keyboard input and to auto-hide when the
36 37 text edit loses focus.
37 38 """
38 39 if obj == self._text_edit:
39 40 etype = event.type()
40 41
41 42 if etype == QtCore.QEvent.KeyPress:
42 43 key, text = event.key(), event.text()
43 44 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter,
44 45 QtCore.Qt.Key_Tab):
45 46 self._complete_current()
46 47 return True
47 48 elif key == QtCore.Qt.Key_Escape:
48 49 self.hide()
49 50 return True
50 51 elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down,
51 52 QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown,
52 53 QtCore.Qt.Key_Home, QtCore.Qt.Key_End):
53 54 self.keyPressEvent(event)
54 55 return True
55 56
56 57 elif etype == QtCore.QEvent.FocusOut:
57 58 self.hide()
58 59
59 60 return super(CompletionWidget, self).eventFilter(obj, event)
60 61
61 62 #--------------------------------------------------------------------------
62 63 # 'QWidget' interface
63 64 #--------------------------------------------------------------------------
64 65
65 66 def hideEvent(self, event):
66 67 """ Reimplemented to disconnect signal handlers and event filter.
67 68 """
68 69 super(CompletionWidget, self).hideEvent(event)
69 70 self._text_edit.cursorPositionChanged.disconnect(self._update_current)
70 71 self._text_edit.removeEventFilter(self)
71 72
72 73 def showEvent(self, event):
73 74 """ Reimplemented to connect signal handlers and event filter.
74 75 """
75 76 super(CompletionWidget, self).showEvent(event)
76 77 self._text_edit.cursorPositionChanged.connect(self._update_current)
77 78 self._text_edit.installEventFilter(self)
78 79
79 80 #--------------------------------------------------------------------------
80 81 # 'CompletionWidget' interface
81 82 #--------------------------------------------------------------------------
82 83
83 84 def show_items(self, cursor, items):
84 85 """ Shows the completion widget with 'items' at the position specified
85 86 by 'cursor'.
86 87 """
87 88 text_edit = self._text_edit
88 89 point = text_edit.cursorRect(cursor).bottomRight()
89 90 point = text_edit.mapToGlobal(point)
90 91 height = self.sizeHint().height()
91 92 screen_rect = QtGui.QApplication.desktop().availableGeometry(self)
92 93 if screen_rect.size().height() - point.y() - height < 0:
93 94 point = text_edit.mapToGlobal(text_edit.cursorRect().topRight())
94 95 point.setY(point.y() - height)
95 96 self.move(point)
96 97
97 98 self._start_position = cursor.position()
98 99 self.clear()
99 100 self.addItems(items)
100 101 self.setCurrentRow(0)
101 102 self.show()
102 103
103 104 #--------------------------------------------------------------------------
104 105 # Protected interface
105 106 #--------------------------------------------------------------------------
106 107
107 108 def _complete_current(self):
108 109 """ Perform the completion with the currently selected item.
109 110 """
110 111 self._current_text_cursor().insertText(self.currentItem().text())
111 112 self.hide()
112 113
113 114 def _current_text_cursor(self):
114 115 """ Returns a cursor with text between the start position and the
115 116 current position selected.
116 117 """
117 118 cursor = self._text_edit.textCursor()
118 119 if cursor.position() >= self._start_position:
119 120 cursor.setPosition(self._start_position,
120 121 QtGui.QTextCursor.KeepAnchor)
121 122 return cursor
122 123
123 124 def _update_current(self):
124 125 """ Updates the current item based on the current text.
125 126 """
126 127 prefix = self._current_text_cursor().selection().toPlainText()
127 128 if prefix:
128 129 items = self.findItems(prefix, (QtCore.Qt.MatchStartsWith |
129 130 QtCore.Qt.MatchCaseSensitive))
130 131 if items:
131 132 self.setCurrentItem(items[0])
132 133 else:
133 134 self.hide()
134 135 else:
135 136 self.hide()
136 137
137 138 def cancel_completion(self):
138 139 self.hide()
@@ -1,584 +1,585 b''
1 """ A FrontendWidget that emulates the interface of the console IPython and
2 supports the additional functionality provided by the IPython kernel.
1 """A FrontendWidget that emulates the interface of the console IPython.
2
3 This supports the additional functionality provided by the IPython kernel.
3 4 """
4 5
5 6 #-----------------------------------------------------------------------------
6 7 # Imports
7 8 #-----------------------------------------------------------------------------
8 9
9 10 # Standard library imports
10 11 from collections import namedtuple
11 12 import os.path
12 13 import re
13 14 from subprocess import Popen
14 15 import sys
15 16 import time
16 17 from textwrap import dedent
17 18
18 19 # System library imports
19 20 from IPython.external.qt import QtCore, QtGui
20 21
21 22 # Local imports
22 23 from IPython.core.inputsplitter import IPythonInputSplitter
23 24 from IPython.core.inputtransformer import ipy_prompt
24 25 from IPython.utils.traitlets import Bool, Unicode
25 26 from .frontend_widget import FrontendWidget
26 27 from . import styles
27 28
28 29 #-----------------------------------------------------------------------------
29 30 # Constants
30 31 #-----------------------------------------------------------------------------
31 32
32 33 # Default strings to build and display input and output prompts (and separators
33 34 # in between)
34 35 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
35 36 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
36 37 default_input_sep = '\n'
37 38 default_output_sep = ''
38 39 default_output_sep2 = ''
39 40
40 41 # Base path for most payload sources.
41 42 zmq_shell_source = 'IPython.kernel.zmq.zmqshell.ZMQInteractiveShell'
42 43
43 44 if sys.platform.startswith('win'):
44 45 default_editor = 'notepad'
45 46 else:
46 47 default_editor = ''
47 48
48 49 #-----------------------------------------------------------------------------
49 50 # IPythonWidget class
50 51 #-----------------------------------------------------------------------------
51 52
52 53 class IPythonWidget(FrontendWidget):
53 54 """ A FrontendWidget for an IPython kernel.
54 55 """
55 56
56 57 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
57 58 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
58 59 # settings.
59 60 custom_edit = Bool(False)
60 61 custom_edit_requested = QtCore.Signal(object, object)
61 62
62 63 editor = Unicode(default_editor, config=True,
63 64 help="""
64 65 A command for invoking a system text editor. If the string contains a
65 66 {filename} format specifier, it will be used. Otherwise, the filename
66 67 will be appended to the end the command.
67 68 """)
68 69
69 70 editor_line = Unicode(config=True,
70 71 help="""
71 72 The editor command to use when a specific line number is requested. The
72 73 string should contain two format specifiers: {line} and {filename}. If
73 74 this parameter is not specified, the line number option to the %edit
74 75 magic will be ignored.
75 76 """)
76 77
77 78 style_sheet = Unicode(config=True,
78 79 help="""
79 80 A CSS stylesheet. The stylesheet can contain classes for:
80 81 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
81 82 2. Pygments: .c, .k, .o, etc. (see PygmentsHighlighter)
82 83 3. IPython: .error, .in-prompt, .out-prompt, etc
83 84 """)
84 85
85 86 syntax_style = Unicode(config=True,
86 87 help="""
87 88 If not empty, use this Pygments style for syntax highlighting.
88 89 Otherwise, the style sheet is queried for Pygments style
89 90 information.
90 91 """)
91 92
92 93 # Prompts.
93 94 in_prompt = Unicode(default_in_prompt, config=True)
94 95 out_prompt = Unicode(default_out_prompt, config=True)
95 96 input_sep = Unicode(default_input_sep, config=True)
96 97 output_sep = Unicode(default_output_sep, config=True)
97 98 output_sep2 = Unicode(default_output_sep2, config=True)
98 99
99 100 # FrontendWidget protected class variables.
100 101 _input_splitter_class = IPythonInputSplitter
101 102 _prompt_transformer = IPythonInputSplitter(physical_line_transforms=[ipy_prompt()],
102 103 logical_line_transforms=[],
103 104 python_line_transforms=[],
104 105 )
105 106
106 107 # IPythonWidget protected class variables.
107 108 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
108 109 _payload_source_edit = 'edit_magic'
109 110 _payload_source_exit = 'ask_exit'
110 111 _payload_source_next_input = 'set_next_input'
111 112 _payload_source_page = 'page'
112 113 _retrying_history_request = False
113 114
114 115 #---------------------------------------------------------------------------
115 116 # 'object' interface
116 117 #---------------------------------------------------------------------------
117 118
118 119 def __init__(self, *args, **kw):
119 120 super(IPythonWidget, self).__init__(*args, **kw)
120 121
121 122 # IPythonWidget protected variables.
122 123 self._payload_handlers = {
123 124 self._payload_source_edit : self._handle_payload_edit,
124 125 self._payload_source_exit : self._handle_payload_exit,
125 126 self._payload_source_page : self._handle_payload_page,
126 127 self._payload_source_next_input : self._handle_payload_next_input }
127 128 self._previous_prompt_obj = None
128 129 self._keep_kernel_on_exit = None
129 130
130 131 # Initialize widget styling.
131 132 if self.style_sheet:
132 133 self._style_sheet_changed()
133 134 self._syntax_style_changed()
134 135 else:
135 136 self.set_default_style()
136 137
137 138 #---------------------------------------------------------------------------
138 139 # 'BaseFrontendMixin' abstract interface
139 140 #---------------------------------------------------------------------------
140 141
141 142 def _handle_complete_reply(self, rep):
142 143 """ Reimplemented to support IPython's improved completion machinery.
143 144 """
144 145 self.log.debug("complete: %s", rep.get('content', ''))
145 146 cursor = self._get_cursor()
146 147 info = self._request_info.get('complete')
147 148 if info and info.id == rep['parent_header']['msg_id'] and \
148 149 info.pos == cursor.position():
149 150 matches = rep['content']['matches']
150 151 text = rep['content']['matched_text']
151 152 offset = len(text)
152 153
153 154 # Clean up matches with period and path separators if the matched
154 155 # text has not been transformed. This is done by truncating all
155 156 # but the last component and then suitably decreasing the offset
156 157 # between the current cursor position and the start of completion.
157 158 if len(matches) > 1 and matches[0][:offset] == text:
158 159 parts = re.split(r'[./\\]', text)
159 160 sep_count = len(parts) - 1
160 161 if sep_count:
161 162 chop_length = sum(map(len, parts[:sep_count])) + sep_count
162 163 matches = [ match[chop_length:] for match in matches ]
163 164 offset -= chop_length
164 165
165 166 # Move the cursor to the start of the match and complete.
166 167 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
167 168 self._complete_with_items(cursor, matches)
168 169
169 170 def _handle_execute_reply(self, msg):
170 171 """ Reimplemented to support prompt requests.
171 172 """
172 173 msg_id = msg['parent_header'].get('msg_id')
173 174 info = self._request_info['execute'].get(msg_id)
174 175 if info and info.kind == 'prompt':
175 176 number = msg['content']['execution_count'] + 1
176 177 self._show_interpreter_prompt(number)
177 178 self._request_info['execute'].pop(msg_id)
178 179 else:
179 180 super(IPythonWidget, self)._handle_execute_reply(msg)
180 181
181 182 def _handle_history_reply(self, msg):
182 183 """ Implemented to handle history tail replies, which are only supported
183 184 by the IPython kernel.
184 185 """
185 186 content = msg['content']
186 187 if 'history' not in content:
187 188 self.log.error("History request failed: %r"%content)
188 189 if content.get('status', '') == 'aborted' and \
189 190 not self._retrying_history_request:
190 191 # a *different* action caused this request to be aborted, so
191 192 # we should try again.
192 193 self.log.error("Retrying aborted history request")
193 194 # prevent multiple retries of aborted requests:
194 195 self._retrying_history_request = True
195 196 # wait out the kernel's queue flush, which is currently timed at 0.1s
196 197 time.sleep(0.25)
197 198 self.kernel_client.shell_channel.history(hist_access_type='tail',n=1000)
198 199 else:
199 200 self._retrying_history_request = False
200 201 return
201 202 # reset retry flag
202 203 self._retrying_history_request = False
203 204 history_items = content['history']
204 205 self.log.debug("Received history reply with %i entries", len(history_items))
205 206 items = []
206 207 last_cell = u""
207 208 for _, _, cell in history_items:
208 209 cell = cell.rstrip()
209 210 if cell != last_cell:
210 211 items.append(cell)
211 212 last_cell = cell
212 213 self._set_history(items)
213 214
214 215 def _handle_pyout(self, msg):
215 216 """ Reimplemented for IPython-style "display hook".
216 217 """
217 218 self.log.debug("pyout: %s", msg.get('content', ''))
218 219 if not self._hidden and self._is_from_this_session(msg):
219 220 content = msg['content']
220 221 prompt_number = content.get('execution_count', 0)
221 222 data = content['data']
222 223 if 'text/html' in data:
223 224 self._append_plain_text(self.output_sep, True)
224 225 self._append_html(self._make_out_prompt(prompt_number), True)
225 226 html = data['text/html']
226 227 self._append_plain_text('\n', True)
227 228 self._append_html(html + self.output_sep2, True)
228 229 elif 'text/plain' in data:
229 230 self._append_plain_text(self.output_sep, True)
230 231 self._append_html(self._make_out_prompt(prompt_number), True)
231 232 text = data['text/plain']
232 233 # If the repr is multiline, make sure we start on a new line,
233 234 # so that its lines are aligned.
234 235 if "\n" in text and not self.output_sep.endswith("\n"):
235 236 self._append_plain_text('\n', True)
236 237 self._append_plain_text(text + self.output_sep2, True)
237 238
238 239 def _handle_display_data(self, msg):
239 240 """ The base handler for the ``display_data`` message.
240 241 """
241 242 self.log.debug("display: %s", msg.get('content', ''))
242 243 # For now, we don't display data from other frontends, but we
243 244 # eventually will as this allows all frontends to monitor the display
244 245 # data. But we need to figure out how to handle this in the GUI.
245 246 if not self._hidden and self._is_from_this_session(msg):
246 247 source = msg['content']['source']
247 248 data = msg['content']['data']
248 249 metadata = msg['content']['metadata']
249 250 # In the regular IPythonWidget, we simply print the plain text
250 251 # representation.
251 252 if 'text/html' in data:
252 253 html = data['text/html']
253 254 self._append_html(html, True)
254 255 elif 'text/plain' in data:
255 256 text = data['text/plain']
256 257 self._append_plain_text(text, True)
257 258 # This newline seems to be needed for text and html output.
258 259 self._append_plain_text(u'\n', True)
259 260
260 261 def _started_channels(self):
261 262 """Reimplemented to make a history request and load %guiref."""
262 263 super(IPythonWidget, self)._started_channels()
263 264 self._load_guiref_magic()
264 265 self.kernel_client.shell_channel.history(hist_access_type='tail',
265 266 n=1000)
266 267
267 268 def _started_kernel(self):
268 269 """Load %guiref when the kernel starts (if channels are also started).
269 270
270 271 Principally triggered by kernel restart.
271 272 """
272 273 if self.kernel_client.shell_channel is not None:
273 274 self._load_guiref_magic()
274 275
275 276 def _load_guiref_magic(self):
276 277 """Load %guiref magic."""
277 278 self.kernel_client.shell_channel.execute('\n'.join([
278 279 "try:",
279 280 " _usage",
280 281 "except:",
281 282 " from IPython.core import usage as _usage",
282 283 " get_ipython().register_magic_function(_usage.page_guiref, 'line', 'guiref')",
283 284 " del _usage",
284 285 ]), silent=True)
285 286
286 287 #---------------------------------------------------------------------------
287 288 # 'ConsoleWidget' public interface
288 289 #---------------------------------------------------------------------------
289 290
290 291 #---------------------------------------------------------------------------
291 292 # 'FrontendWidget' public interface
292 293 #---------------------------------------------------------------------------
293 294
294 295 def execute_file(self, path, hidden=False):
295 296 """ Reimplemented to use the 'run' magic.
296 297 """
297 298 # Use forward slashes on Windows to avoid escaping each separator.
298 299 if sys.platform == 'win32':
299 300 path = os.path.normpath(path).replace('\\', '/')
300 301
301 302 # Perhaps we should not be using %run directly, but while we
302 303 # are, it is necessary to quote or escape filenames containing spaces
303 304 # or quotes.
304 305
305 306 # In earlier code here, to minimize escaping, we sometimes quoted the
306 307 # filename with single quotes. But to do this, this code must be
307 308 # platform-aware, because run uses shlex rather than python string
308 309 # parsing, so that:
309 310 # * In Win: single quotes can be used in the filename without quoting,
310 311 # and we cannot use single quotes to quote the filename.
311 312 # * In *nix: we can escape double quotes in a double quoted filename,
312 313 # but can't escape single quotes in a single quoted filename.
313 314
314 315 # So to keep this code non-platform-specific and simple, we now only
315 316 # use double quotes to quote filenames, and escape when needed:
316 317 if ' ' in path or "'" in path or '"' in path:
317 318 path = '"%s"' % path.replace('"', '\\"')
318 319 self.execute('%%run %s' % path, hidden=hidden)
319 320
320 321 #---------------------------------------------------------------------------
321 322 # 'FrontendWidget' protected interface
322 323 #---------------------------------------------------------------------------
323 324
324 325 def _complete(self):
325 326 """ Reimplemented to support IPython's improved completion machinery.
326 327 """
327 328 # We let the kernel split the input line, so we *always* send an empty
328 329 # text field. Readline-based frontends do get a real text field which
329 330 # they can use.
330 331 text = ''
331 332
332 333 # Send the completion request to the kernel
333 334 msg_id = self.kernel_client.shell_channel.complete(
334 335 text, # text
335 336 self._get_input_buffer_cursor_line(), # line
336 337 self._get_input_buffer_cursor_column(), # cursor_pos
337 338 self.input_buffer) # block
338 339 pos = self._get_cursor().position()
339 340 info = self._CompletionRequest(msg_id, pos)
340 341 self._request_info['complete'] = info
341 342
342 343 def _process_execute_error(self, msg):
343 344 """ Reimplemented for IPython-style traceback formatting.
344 345 """
345 346 content = msg['content']
346 347 traceback = '\n'.join(content['traceback']) + '\n'
347 348 if False:
348 349 # FIXME: For now, tracebacks come as plain text, so we can't use
349 350 # the html renderer yet. Once we refactor ultratb to produce
350 351 # properly styled tracebacks, this branch should be the default
351 352 traceback = traceback.replace(' ', '&nbsp;')
352 353 traceback = traceback.replace('\n', '<br/>')
353 354
354 355 ename = content['ename']
355 356 ename_styled = '<span class="error">%s</span>' % ename
356 357 traceback = traceback.replace(ename, ename_styled)
357 358
358 359 self._append_html(traceback)
359 360 else:
360 361 # This is the fallback for now, using plain text with ansi escapes
361 362 self._append_plain_text(traceback)
362 363
363 364 def _process_execute_payload(self, item):
364 365 """ Reimplemented to dispatch payloads to handler methods.
365 366 """
366 367 handler = self._payload_handlers.get(item['source'])
367 368 if handler is None:
368 369 # We have no handler for this type of payload, simply ignore it
369 370 return False
370 371 else:
371 372 handler(item)
372 373 return True
373 374
374 375 def _show_interpreter_prompt(self, number=None):
375 376 """ Reimplemented for IPython-style prompts.
376 377 """
377 378 # If a number was not specified, make a prompt number request.
378 379 if number is None:
379 380 msg_id = self.kernel_client.shell_channel.execute('', silent=True)
380 381 info = self._ExecutionRequest(msg_id, 'prompt')
381 382 self._request_info['execute'][msg_id] = info
382 383 return
383 384
384 385 # Show a new prompt and save information about it so that it can be
385 386 # updated later if the prompt number turns out to be wrong.
386 387 self._prompt_sep = self.input_sep
387 388 self._show_prompt(self._make_in_prompt(number), html=True)
388 389 block = self._control.document().lastBlock()
389 390 length = len(self._prompt)
390 391 self._previous_prompt_obj = self._PromptBlock(block, length, number)
391 392
392 393 # Update continuation prompt to reflect (possibly) new prompt length.
393 394 self._set_continuation_prompt(
394 395 self._make_continuation_prompt(self._prompt), html=True)
395 396
396 397 def _show_interpreter_prompt_for_reply(self, msg):
397 398 """ Reimplemented for IPython-style prompts.
398 399 """
399 400 # Update the old prompt number if necessary.
400 401 content = msg['content']
401 402 # abort replies do not have any keys:
402 403 if content['status'] == 'aborted':
403 404 if self._previous_prompt_obj:
404 405 previous_prompt_number = self._previous_prompt_obj.number
405 406 else:
406 407 previous_prompt_number = 0
407 408 else:
408 409 previous_prompt_number = content['execution_count']
409 410 if self._previous_prompt_obj and \
410 411 self._previous_prompt_obj.number != previous_prompt_number:
411 412 block = self._previous_prompt_obj.block
412 413
413 414 # Make sure the prompt block has not been erased.
414 415 if block.isValid() and block.text():
415 416
416 417 # Remove the old prompt and insert a new prompt.
417 418 cursor = QtGui.QTextCursor(block)
418 419 cursor.movePosition(QtGui.QTextCursor.Right,
419 420 QtGui.QTextCursor.KeepAnchor,
420 421 self._previous_prompt_obj.length)
421 422 prompt = self._make_in_prompt(previous_prompt_number)
422 423 self._prompt = self._insert_html_fetching_plain_text(
423 424 cursor, prompt)
424 425
425 426 # When the HTML is inserted, Qt blows away the syntax
426 427 # highlighting for the line, so we need to rehighlight it.
427 428 self._highlighter.rehighlightBlock(cursor.block())
428 429
429 430 self._previous_prompt_obj = None
430 431
431 432 # Show a new prompt with the kernel's estimated prompt number.
432 433 self._show_interpreter_prompt(previous_prompt_number + 1)
433 434
434 435 #---------------------------------------------------------------------------
435 436 # 'IPythonWidget' interface
436 437 #---------------------------------------------------------------------------
437 438
438 439 def set_default_style(self, colors='lightbg'):
439 440 """ Sets the widget style to the class defaults.
440 441
441 442 Parameters
442 443 ----------
443 444 colors : str, optional (default lightbg)
444 445 Whether to use the default IPython light background or dark
445 446 background or B&W style.
446 447 """
447 448 colors = colors.lower()
448 449 if colors=='lightbg':
449 450 self.style_sheet = styles.default_light_style_sheet
450 451 self.syntax_style = styles.default_light_syntax_style
451 452 elif colors=='linux':
452 453 self.style_sheet = styles.default_dark_style_sheet
453 454 self.syntax_style = styles.default_dark_syntax_style
454 455 elif colors=='nocolor':
455 456 self.style_sheet = styles.default_bw_style_sheet
456 457 self.syntax_style = styles.default_bw_syntax_style
457 458 else:
458 459 raise KeyError("No such color scheme: %s"%colors)
459 460
460 461 #---------------------------------------------------------------------------
461 462 # 'IPythonWidget' protected interface
462 463 #---------------------------------------------------------------------------
463 464
464 465 def _edit(self, filename, line=None):
465 466 """ Opens a Python script for editing.
466 467
467 468 Parameters
468 469 ----------
469 470 filename : str
470 471 A path to a local system file.
471 472
472 473 line : int, optional
473 474 A line of interest in the file.
474 475 """
475 476 if self.custom_edit:
476 477 self.custom_edit_requested.emit(filename, line)
477 478 elif not self.editor:
478 479 self._append_plain_text('No default editor available.\n'
479 480 'Specify a GUI text editor in the `IPythonWidget.editor` '
480 481 'configurable to enable the %edit magic')
481 482 else:
482 483 try:
483 484 filename = '"%s"' % filename
484 485 if line and self.editor_line:
485 486 command = self.editor_line.format(filename=filename,
486 487 line=line)
487 488 else:
488 489 try:
489 490 command = self.editor.format()
490 491 except KeyError:
491 492 command = self.editor.format(filename=filename)
492 493 else:
493 494 command += ' ' + filename
494 495 except KeyError:
495 496 self._append_plain_text('Invalid editor command.\n')
496 497 else:
497 498 try:
498 499 Popen(command, shell=True)
499 500 except OSError:
500 501 msg = 'Opening editor with command "%s" failed.\n'
501 502 self._append_plain_text(msg % command)
502 503
503 504 def _make_in_prompt(self, number):
504 505 """ Given a prompt number, returns an HTML In prompt.
505 506 """
506 507 try:
507 508 body = self.in_prompt % number
508 509 except TypeError:
509 510 # allow in_prompt to leave out number, e.g. '>>> '
510 511 body = self.in_prompt
511 512 return '<span class="in-prompt">%s</span>' % body
512 513
513 514 def _make_continuation_prompt(self, prompt):
514 515 """ Given a plain text version of an In prompt, returns an HTML
515 516 continuation prompt.
516 517 """
517 518 end_chars = '...: '
518 519 space_count = len(prompt.lstrip('\n')) - len(end_chars)
519 520 body = '&nbsp;' * space_count + end_chars
520 521 return '<span class="in-prompt">%s</span>' % body
521 522
522 523 def _make_out_prompt(self, number):
523 524 """ Given a prompt number, returns an HTML Out prompt.
524 525 """
525 526 body = self.out_prompt % number
526 527 return '<span class="out-prompt">%s</span>' % body
527 528
528 529 #------ Payload handlers --------------------------------------------------
529 530
530 531 # Payload handlers with a generic interface: each takes the opaque payload
531 532 # dict, unpacks it and calls the underlying functions with the necessary
532 533 # arguments.
533 534
534 535 def _handle_payload_edit(self, item):
535 536 self._edit(item['filename'], item['line_number'])
536 537
537 538 def _handle_payload_exit(self, item):
538 539 self._keep_kernel_on_exit = item['keepkernel']
539 540 self.exit_requested.emit(self)
540 541
541 542 def _handle_payload_next_input(self, item):
542 543 self.input_buffer = item['text']
543 544
544 545 def _handle_payload_page(self, item):
545 546 # Since the plain text widget supports only a very small subset of HTML
546 547 # and we have no control over the HTML source, we only page HTML
547 548 # payloads in the rich text widget.
548 549 if item['html'] and self.kind == 'rich':
549 550 self._page(item['html'], html=True)
550 551 else:
551 552 self._page(item['text'], html=False)
552 553
553 554 #------ Trait change handlers --------------------------------------------
554 555
555 556 def _style_sheet_changed(self):
556 557 """ Set the style sheets of the underlying widgets.
557 558 """
558 559 self.setStyleSheet(self.style_sheet)
559 560 if self._control is not None:
560 561 self._control.document().setDefaultStyleSheet(self.style_sheet)
561 562 bg_color = self._control.palette().window().color()
562 563 self._ansi_processor.set_background_color(bg_color)
563 564
564 565 if self._page_control is not None:
565 566 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
566 567
567 568
568 569
569 570 def _syntax_style_changed(self):
570 571 """ Set the style for the syntax highlighter.
571 572 """
572 573 if self._highlighter is None:
573 574 # ignore premature calls
574 575 return
575 576 if self.syntax_style:
576 577 self._highlighter.set_style(self.syntax_style)
577 578 else:
578 579 self._highlighter.set_style_sheet(self.style_sheet)
579 580
580 581 #------ Trait default initializers -----------------------------------------
581 582
582 583 def _banner_default(self):
583 584 from IPython.core.usage import default_gui_banner
584 585 return default_gui_banner
@@ -1,55 +1,57 b''
1 """Adapt readline completer interface to make ZMQ request.
2 """
1 3 # -*- coding: utf-8 -*-
2 4 import readline
3 5 try:
4 6 from queue import Empty # Py 3
5 7 except ImportError:
6 8 from Queue import Empty # Py 2
7 9
8 10 from IPython.config import Configurable
9 11 from IPython.utils.traitlets import Float
10 12
11 13 class ZMQCompleter(Configurable):
12 14 """Client-side completion machinery.
13 15
14 16 How it works: self.complete will be called multiple times, with
15 17 state=0,1,2,... When state=0 it should compute ALL the completion matches,
16 18 and then return them for each value of state."""
17 19
18 20 timeout = Float(5.0, config=True, help='timeout before completion abort')
19 21
20 22 def __init__(self, shell, client, config=None):
21 23 super(ZMQCompleter,self).__init__(config=config)
22 24
23 25 self.shell = shell
24 26 self.client = client
25 27 self.matches = []
26 28
27 29 def complete_request(self,text):
28 30 line = readline.get_line_buffer()
29 31 cursor_pos = readline.get_endidx()
30 32
31 33 # send completion request to kernel
32 34 # Give the kernel up to 0.5s to respond
33 35 msg_id = self.client.shell_channel.complete(text=text, line=line,
34 36 cursor_pos=cursor_pos)
35 37
36 38 msg = self.client.shell_channel.get_msg(timeout=self.timeout)
37 39 if msg['parent_header']['msg_id'] == msg_id:
38 40 return msg["content"]["matches"]
39 41 return []
40 42
41 43 def rlcomplete(self, text, state):
42 44 if state == 0:
43 45 try:
44 46 self.matches = self.complete_request(text)
45 47 except Empty:
46 48 #print('WARNING: Kernel timeout on tab completion.')
47 49 pass
48 50
49 51 try:
50 52 return self.matches[state]
51 53 except IndexError:
52 54 return None
53 55
54 56 def complete(self, text, line, cursor_pos=None):
55 57 return self.rlcomplete(text, 0)
@@ -1,46 +1,46 b''
1 """utilities for checking zmq versions"""
1 """Utilities for checking zmq versions."""
2 2 #-----------------------------------------------------------------------------
3 3 # Copyright (C) 2013 The IPython Development Team
4 4 #
5 5 # Distributed under the terms of the BSD License. The full license is in
6 6 # the file COPYING.txt, distributed as part of this software.
7 7 #-----------------------------------------------------------------------------
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Verify zmq version dependency >= 2.1.11
11 11 #-----------------------------------------------------------------------------
12 12
13 13 from IPython.utils.version import check_version
14 14
15 15
16 16 def patch_pyzmq():
17 17 """backport a few patches from newer pyzmq
18 18
19 19 These can be removed as we bump our minimum pyzmq version
20 20 """
21 21
22 22 import zmq
23 23
24 24 # fallback on stdlib json if jsonlib is selected, because jsonlib breaks things.
25 25 # jsonlib support is removed from pyzmq >= 2.2.0
26 26
27 27 from zmq.utils import jsonapi
28 28 if jsonapi.jsonmod.__name__ == 'jsonlib':
29 29 import json
30 30 jsonapi.jsonmod = json
31 31
32 32
33 33 def check_for_zmq(minimum_version, required_by='Someone'):
34 34 try:
35 35 import zmq
36 36 except ImportError:
37 37 raise ImportError("%s requires pyzmq >= %s"%(required_by, minimum_version))
38 38
39 39 patch_pyzmq()
40 40
41 41 pyzmq_version = zmq.__version__
42 42
43 43 if not check_version(pyzmq_version, minimum_version):
44 44 raise ImportError("%s requires pyzmq >= %s, but you have %s"%(
45 45 required_by, minimum_version, pyzmq_version))
46 46
General Comments 0
You need to be logged in to leave comments. Login now