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