##// END OF EJS Templates
determine encoding of source lines in %debug before colorizing
Jörgen Stenarson -
Show More
@@ -1,527 +1,529 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Pdb debugger class.
4 4
5 5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 6 the command line completion of other programs which include this isn't
7 7 damaged.
8 8
9 9 In the future, this class will be expanded with improvements over the standard
10 10 pdb.
11 11
12 12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 13 changes. Licensing should therefore be under the standard Python terms. For
14 14 details on the PSF (Python Software Foundation) standard license, see:
15 15
16 16 http://www.python.org/2.2.3/license.html"""
17 17
18 18 #*****************************************************************************
19 19 #
20 20 # This file is licensed under the PSF license.
21 21 #
22 22 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 23 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 24 #
25 25 #
26 26 #*****************************************************************************
27 27 from __future__ import print_function
28 28
29 29 import bdb
30 30 import linecache
31 31 import sys
32 32
33 33 from IPython.utils import PyColorize
34 34 from IPython.core import ipapi
35 35 from IPython.utils import coloransi, io
36 36 from IPython.core.excolors import exception_colors
37 37
38 38 # See if we can use pydb.
39 39 has_pydb = False
40 40 prompt = 'ipdb> '
41 41 #We have to check this directly from sys.argv, config struct not yet available
42 42 if '--pydb' in sys.argv:
43 43 try:
44 44 import pydb
45 45 if hasattr(pydb.pydb, "runl") and pydb.version>'1.17':
46 46 # Version 1.17 is broken, and that's what ships with Ubuntu Edgy, so we
47 47 # better protect against it.
48 48 has_pydb = True
49 49 except ImportError:
50 50 print("Pydb (http://bashdb.sourceforge.net/pydb/) does not seem to be available")
51 51
52 52 if has_pydb:
53 53 from pydb import Pdb as OldPdb
54 54 #print "Using pydb for %run -d and post-mortem" #dbg
55 55 prompt = 'ipydb> '
56 56 else:
57 57 from pdb import Pdb as OldPdb
58 58
59 59 # Allow the set_trace code to operate outside of an ipython instance, even if
60 60 # it does so with some limitations. The rest of this support is implemented in
61 61 # the Tracer constructor.
62 62 def BdbQuit_excepthook(et,ev,tb):
63 63 if et==bdb.BdbQuit:
64 64 print('Exiting Debugger.')
65 65 else:
66 66 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
67 67
68 68 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
69 69 print('Exiting Debugger.')
70 70
71 71
72 72 class Tracer(object):
73 73 """Class for local debugging, similar to pdb.set_trace.
74 74
75 75 Instances of this class, when called, behave like pdb.set_trace, but
76 76 providing IPython's enhanced capabilities.
77 77
78 78 This is implemented as a class which must be initialized in your own code
79 79 and not as a standalone function because we need to detect at runtime
80 80 whether IPython is already active or not. That detection is done in the
81 81 constructor, ensuring that this code plays nicely with a running IPython,
82 82 while functioning acceptably (though with limitations) if outside of it.
83 83 """
84 84
85 85 def __init__(self,colors=None):
86 86 """Create a local debugger instance.
87 87
88 88 :Parameters:
89 89
90 90 - `colors` (None): a string containing the name of the color scheme to
91 91 use, it must be one of IPython's valid color schemes. If not given, the
92 92 function will default to the current IPython scheme when running inside
93 93 IPython, and to 'NoColor' otherwise.
94 94
95 95 Usage example:
96 96
97 97 from IPython.core.debugger import Tracer; debug_here = Tracer()
98 98
99 99 ... later in your code
100 100 debug_here() # -> will open up the debugger at that point.
101 101
102 102 Once the debugger activates, you can use all of its regular commands to
103 103 step through code, set breakpoints, etc. See the pdb documentation
104 104 from the Python standard library for usage details.
105 105 """
106 106
107 107 try:
108 108 ip = get_ipython()
109 109 except NameError:
110 110 # Outside of ipython, we set our own exception hook manually
111 111 BdbQuit_excepthook.excepthook_ori = sys.excepthook
112 112 sys.excepthook = BdbQuit_excepthook
113 113 def_colors = 'NoColor'
114 114 try:
115 115 # Limited tab completion support
116 116 import readline
117 117 readline.parse_and_bind('tab: complete')
118 118 except ImportError:
119 119 pass
120 120 else:
121 121 # In ipython, we use its custom exception handler mechanism
122 122 def_colors = ip.colors
123 123 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
124 124
125 125 if colors is None:
126 126 colors = def_colors
127 127
128 128 # The stdlib debugger internally uses a modified repr from the `repr`
129 129 # module, that limits the length of printed strings to a hardcoded
130 130 # limit of 30 characters. That much trimming is too aggressive, let's
131 131 # at least raise that limit to 80 chars, which should be enough for
132 132 # most interactive uses.
133 133 try:
134 134 from repr import aRepr
135 135 aRepr.maxstring = 80
136 136 except:
137 137 # This is only a user-facing convenience, so any error we encounter
138 138 # here can be warned about but can be otherwise ignored. These
139 139 # printouts will tell us about problems if this API changes
140 140 import traceback
141 141 traceback.print_exc()
142 142
143 143 self.debugger = Pdb(colors)
144 144
145 145 def __call__(self):
146 146 """Starts an interactive debugger at the point where called.
147 147
148 148 This is similar to the pdb.set_trace() function from the std lib, but
149 149 using IPython's enhanced debugger."""
150 150
151 151 self.debugger.set_trace(sys._getframe().f_back)
152 152
153 153
154 154 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
155 155 """Make new_fn have old_fn's doc string. This is particularly useful
156 156 for the do_... commands that hook into the help system.
157 157 Adapted from from a comp.lang.python posting
158 158 by Duncan Booth."""
159 159 def wrapper(*args, **kw):
160 160 return new_fn(*args, **kw)
161 161 if old_fn.__doc__:
162 162 wrapper.__doc__ = old_fn.__doc__ + additional_text
163 163 return wrapper
164 164
165 165
166 166 def _file_lines(fname):
167 167 """Return the contents of a named file as a list of lines.
168 168
169 169 This function never raises an IOError exception: if the file can't be
170 170 read, it simply returns an empty list."""
171 171
172 172 try:
173 173 outfile = open(fname)
174 174 except IOError:
175 175 return []
176 176 else:
177 177 out = outfile.readlines()
178 178 outfile.close()
179 179 return out
180 180
181 181
182 182 class Pdb(OldPdb):
183 183 """Modified Pdb class, does not load readline."""
184 184
185 185 def __init__(self,color_scheme='NoColor',completekey=None,
186 186 stdin=None, stdout=None):
187 187
188 188 # Parent constructor:
189 189 if has_pydb and completekey is None:
190 190 OldPdb.__init__(self,stdin=stdin,stdout=io.stdout)
191 191 else:
192 192 OldPdb.__init__(self,completekey,stdin,stdout)
193 193
194 194 self.prompt = prompt # The default prompt is '(Pdb)'
195 195
196 196 # IPython changes...
197 197 self.is_pydb = has_pydb
198 198
199 199 self.shell = ipapi.get()
200 200
201 201 if self.is_pydb:
202 202
203 203 # interactiveshell.py's ipalias seems to want pdb's checkline
204 204 # which located in pydb.fn
205 205 import pydb.fns
206 206 self.checkline = lambda filename, lineno: \
207 207 pydb.fns.checkline(self, filename, lineno)
208 208
209 209 self.curframe = None
210 210 self.do_restart = self.new_do_restart
211 211
212 212 self.old_all_completions = self.shell.Completer.all_completions
213 213 self.shell.Completer.all_completions=self.all_completions
214 214
215 215 self.do_list = decorate_fn_with_doc(self.list_command_pydb,
216 216 OldPdb.do_list)
217 217 self.do_l = self.do_list
218 218 self.do_frame = decorate_fn_with_doc(self.new_do_frame,
219 219 OldPdb.do_frame)
220 220
221 221 self.aliases = {}
222 222
223 223 # Create color table: we copy the default one from the traceback
224 224 # module and add a few attributes needed for debugging
225 225 self.color_scheme_table = exception_colors()
226 226
227 227 # shorthands
228 228 C = coloransi.TermColors
229 229 cst = self.color_scheme_table
230 230
231 231 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
232 232 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
233 233
234 234 cst['Linux'].colors.breakpoint_enabled = C.LightRed
235 235 cst['Linux'].colors.breakpoint_disabled = C.Red
236 236
237 237 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
238 238 cst['LightBG'].colors.breakpoint_disabled = C.Red
239 239
240 240 self.set_colors(color_scheme)
241 241
242 242 # Add a python parser so we can syntax highlight source while
243 243 # debugging.
244 244 self.parser = PyColorize.Parser()
245 245
246 246 def set_colors(self, scheme):
247 247 """Shorthand access to the color table scheme selector method."""
248 248 self.color_scheme_table.set_active_scheme(scheme)
249 249
250 250 def interaction(self, frame, traceback):
251 251 self.shell.set_completer_frame(frame)
252 252 OldPdb.interaction(self, frame, traceback)
253 253
254 254 def new_do_up(self, arg):
255 255 OldPdb.do_up(self, arg)
256 256 self.shell.set_completer_frame(self.curframe)
257 257 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
258 258
259 259 def new_do_down(self, arg):
260 260 OldPdb.do_down(self, arg)
261 261 self.shell.set_completer_frame(self.curframe)
262 262
263 263 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
264 264
265 265 def new_do_frame(self, arg):
266 266 OldPdb.do_frame(self, arg)
267 267 self.shell.set_completer_frame(self.curframe)
268 268
269 269 def new_do_quit(self, arg):
270 270
271 271 if hasattr(self, 'old_all_completions'):
272 272 self.shell.Completer.all_completions=self.old_all_completions
273 273
274 274
275 275 return OldPdb.do_quit(self, arg)
276 276
277 277 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
278 278
279 279 def new_do_restart(self, arg):
280 280 """Restart command. In the context of ipython this is exactly the same
281 281 thing as 'quit'."""
282 282 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
283 283 return self.do_quit(arg)
284 284
285 285 def postloop(self):
286 286 self.shell.set_completer_frame(None)
287 287
288 288 def print_stack_trace(self):
289 289 try:
290 290 for frame_lineno in self.stack:
291 291 self.print_stack_entry(frame_lineno, context = 5)
292 292 except KeyboardInterrupt:
293 293 pass
294 294
295 295 def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ',
296 296 context = 3):
297 297 #frame, lineno = frame_lineno
298 298 print(self.format_stack_entry(frame_lineno, '', context), file=io.stdout)
299 299
300 300 # vds: >>
301 301 frame, lineno = frame_lineno
302 302 filename = frame.f_code.co_filename
303 303 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
304 304 # vds: <<
305 305
306 306 def format_stack_entry(self, frame_lineno, lprefix=': ', context = 3):
307 307 import linecache, repr
308 308
309 309 ret = []
310 310
311 311 Colors = self.color_scheme_table.active_colors
312 312 ColorsNormal = Colors.Normal
313 313 tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal)
314 314 tpl_call = '%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
315 315 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
316 316 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
317 317 ColorsNormal)
318 318
319 319 frame, lineno = frame_lineno
320 320
321 321 return_value = ''
322 322 if '__return__' in frame.f_locals:
323 323 rv = frame.f_locals['__return__']
324 324 #return_value += '->'
325 325 return_value += repr.repr(rv) + '\n'
326 326 ret.append(return_value)
327 327
328 328 #s = filename + '(' + `lineno` + ')'
329 329 filename = self.canonic(frame.f_code.co_filename)
330 330 link = tpl_link % filename
331 331
332 332 if frame.f_code.co_name:
333 333 func = frame.f_code.co_name
334 334 else:
335 335 func = "<lambda>"
336 336
337 337 call = ''
338 338 if func != '?':
339 339 if '__args__' in frame.f_locals:
340 340 args = repr.repr(frame.f_locals['__args__'])
341 341 else:
342 342 args = '()'
343 343 call = tpl_call % (func, args)
344 344
345 345 # The level info should be generated in the same format pdb uses, to
346 346 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
347 347 if frame is self.curframe:
348 348 ret.append('> ')
349 349 else:
350 350 ret.append(' ')
351 351 ret.append('%s(%s)%s\n' % (link,lineno,call))
352 352
353 353 start = lineno - 1 - context//2
354 354 lines = linecache.getlines(filename)
355 encoding = io.guess_encoding(lines)
355 356 start = max(start, 0)
356 357 start = min(start, len(lines) - context)
357 358 lines = lines[start : start + context]
358 359
359 360 for i,line in enumerate(lines):
360 361 show_arrow = (start + 1 + i == lineno)
361 362 linetpl = (frame is self.curframe or show_arrow) \
362 363 and tpl_line_em \
363 364 or tpl_line
364 365 ret.append(self.__format_line(linetpl, filename,
365 start + 1 + i, line,
366 start + 1 + i, line.decode(encoding),
366 367 arrow = show_arrow) )
367
368 368 return ''.join(ret)
369 369
370 370 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
371 371 bp_mark = ""
372 372 bp_mark_color = ""
373 373
374 374 scheme = self.color_scheme_table.active_scheme_name
375 375 new_line, err = self.parser.format2(line, 'str', scheme)
376 376 if not err: line = new_line
377 377
378 378 bp = None
379 379 if lineno in self.get_file_breaks(filename):
380 380 bps = self.get_breaks(filename, lineno)
381 381 bp = bps[-1]
382 382
383 383 if bp:
384 384 Colors = self.color_scheme_table.active_colors
385 385 bp_mark = str(bp.number)
386 386 bp_mark_color = Colors.breakpoint_enabled
387 387 if not bp.enabled:
388 388 bp_mark_color = Colors.breakpoint_disabled
389 389
390 390 numbers_width = 7
391 391 if arrow:
392 392 # This is the line with the error
393 393 pad = numbers_width - len(str(lineno)) - len(bp_mark)
394 394 if pad >= 3:
395 395 marker = '-'*(pad-3) + '-> '
396 396 elif pad == 2:
397 397 marker = '> '
398 398 elif pad == 1:
399 399 marker = '>'
400 400 else:
401 401 marker = ''
402 402 num = '%s%s' % (marker, str(lineno))
403 403 line = tpl_line % (bp_mark_color + bp_mark, num, line)
404 404 else:
405 405 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
406 406 line = tpl_line % (bp_mark_color + bp_mark, num, line)
407 407
408 408 return line
409 409
410 410 def list_command_pydb(self, arg):
411 411 """List command to use if we have a newer pydb installed"""
412 412 filename, first, last = OldPdb.parse_list_cmd(self, arg)
413 413 if filename is not None:
414 414 self.print_list_lines(filename, first, last)
415 415
416 416 def print_list_lines(self, filename, first, last):
417 417 """The printing (as opposed to the parsing part of a 'list'
418 418 command."""
419 419 try:
420 420 Colors = self.color_scheme_table.active_colors
421 421 ColorsNormal = Colors.Normal
422 422 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
423 423 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
424 424 src = []
425 lines = linecache.getlines(filename)
426 encoding = io.guess_encoding(lines)
425 427 for lineno in range(first, last+1):
426 line = linecache.getline(filename, lineno)
428 line = lines[lineno].decode(encoding)
427 429 if not line:
428 430 break
429 431
430 432 if lineno == self.curframe.f_lineno:
431 433 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
432 434 else:
433 435 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
434 436
435 437 src.append(line)
436 438 self.lineno = lineno
437 439
438 440 print(''.join(src), file=io.stdout)
439 441
440 442 except KeyboardInterrupt:
441 443 pass
442 444
443 445 def do_list(self, arg):
444 446 self.lastcmd = 'list'
445 447 last = None
446 448 if arg:
447 449 try:
448 450 x = eval(arg, {}, {})
449 451 if type(x) == type(()):
450 452 first, last = x
451 453 first = int(first)
452 454 last = int(last)
453 455 if last < first:
454 456 # Assume it's a count
455 457 last = first + last
456 458 else:
457 459 first = max(1, int(x) - 5)
458 460 except:
459 461 print('*** Error in argument:', repr(arg))
460 462 return
461 463 elif self.lineno is None:
462 464 first = max(1, self.curframe.f_lineno - 5)
463 465 else:
464 466 first = self.lineno + 1
465 467 if last is None:
466 468 last = first + 10
467 469 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
468 470
469 471 # vds: >>
470 472 lineno = first
471 473 filename = self.curframe.f_code.co_filename
472 474 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
473 475 # vds: <<
474 476
475 477 do_l = do_list
476 478
477 479 def do_pdef(self, arg):
478 480 """The debugger interface to magic_pdef"""
479 481 namespaces = [('Locals', self.curframe.f_locals),
480 482 ('Globals', self.curframe.f_globals)]
481 483 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
482 484
483 485 def do_pdoc(self, arg):
484 486 """The debugger interface to magic_pdoc"""
485 487 namespaces = [('Locals', self.curframe.f_locals),
486 488 ('Globals', self.curframe.f_globals)]
487 489 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
488 490
489 491 def do_pinfo(self, arg):
490 492 """The debugger equivalant of ?obj"""
491 493 namespaces = [('Locals', self.curframe.f_locals),
492 494 ('Globals', self.curframe.f_globals)]
493 495 self.shell.find_line_magic('pinfo')("pinfo %s" % arg,
494 496 namespaces=namespaces)
495 497
496 498 def checkline(self, filename, lineno):
497 499 """Check whether specified line seems to be executable.
498 500
499 501 Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
500 502 line or EOF). Warning: testing is not comprehensive.
501 503 """
502 504 #######################################################################
503 505 # XXX Hack! Use python-2.5 compatible code for this call, because with
504 506 # all of our changes, we've drifted from the pdb api in 2.6. For now,
505 507 # changing:
506 508 #
507 509 #line = linecache.getline(filename, lineno, self.curframe.f_globals)
508 510 # to:
509 511 #
510 512 line = linecache.getline(filename, lineno)
511 513 #
512 514 # does the trick. But in reality, we need to fix this by reconciling
513 515 # our updates with the new Pdb APIs in Python 2.6.
514 516 #
515 517 # End hack. The rest of this method is copied verbatim from 2.6 pdb.py
516 518 #######################################################################
517 519
518 520 if not line:
519 521 print('End of file', file=self.stdout)
520 522 return 0
521 523 line = line.strip()
522 524 # Don't allow setting breakpoint at a blank line
523 525 if (not line or (line[0] == '#') or
524 526 (line[:3] == '"""') or line[:3] == "'''"):
525 527 print('*** Blank or comment', file=self.stdout)
526 528 return 0
527 529 return lineno
@@ -1,402 +1,409 b''
1 1 # encoding: utf-8
2 2 """
3 3 IO related utilities.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12 from __future__ import print_function
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 import re
18 18 import os
19 19 import sys
20 20 import tempfile
21 21 from StringIO import StringIO
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Code
25 25 #-----------------------------------------------------------------------------
26 26
27 27
28 28 class IOStream:
29 29
30 30 def __init__(self,stream, fallback=None):
31 31 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
32 32 if fallback is not None:
33 33 stream = fallback
34 34 else:
35 35 raise ValueError("fallback required, but not specified")
36 36 self.stream = stream
37 37 self._swrite = stream.write
38 38
39 39 # clone all methods not overridden:
40 40 def clone(meth):
41 41 return not hasattr(self, meth) and not meth.startswith('_')
42 42 for meth in filter(clone, dir(stream)):
43 43 setattr(self, meth, getattr(stream, meth))
44 44
45 45 def write(self,data):
46 46 try:
47 47 self._swrite(data)
48 48 except:
49 49 try:
50 50 # print handles some unicode issues which may trip a plain
51 51 # write() call. Emulate write() by using an empty end
52 52 # argument.
53 53 print(data, end='', file=self.stream)
54 54 except:
55 55 # if we get here, something is seriously broken.
56 56 print('ERROR - failed to write data to stream:', self.stream,
57 57 file=sys.stderr)
58 58
59 59 def writelines(self, lines):
60 60 if isinstance(lines, basestring):
61 61 lines = [lines]
62 62 for line in lines:
63 63 self.write(line)
64 64
65 65 # This class used to have a writeln method, but regular files and streams
66 66 # in Python don't have this method. We need to keep this completely
67 67 # compatible so we removed it.
68 68
69 69 @property
70 70 def closed(self):
71 71 return self.stream.closed
72 72
73 73 def close(self):
74 74 pass
75 75
76 76 # setup stdin/stdout/stderr to sys.stdin/sys.stdout/sys.stderr
77 77 devnull = open(os.devnull, 'a')
78 78 stdin = IOStream(sys.stdin, fallback=devnull)
79 79 stdout = IOStream(sys.stdout, fallback=devnull)
80 80 stderr = IOStream(sys.stderr, fallback=devnull)
81 81
82 82 class IOTerm:
83 83 """ Term holds the file or file-like objects for handling I/O operations.
84 84
85 85 These are normally just sys.stdin, sys.stdout and sys.stderr but for
86 86 Windows they can can replaced to allow editing the strings before they are
87 87 displayed."""
88 88
89 89 # In the future, having IPython channel all its I/O operations through
90 90 # this class will make it easier to embed it into other environments which
91 91 # are not a normal terminal (such as a GUI-based shell)
92 92 def __init__(self, stdin=None, stdout=None, stderr=None):
93 93 mymodule = sys.modules[__name__]
94 94 self.stdin = IOStream(stdin, mymodule.stdin)
95 95 self.stdout = IOStream(stdout, mymodule.stdout)
96 96 self.stderr = IOStream(stderr, mymodule.stderr)
97 97
98 98
99 99 class Tee(object):
100 100 """A class to duplicate an output stream to stdout/err.
101 101
102 102 This works in a manner very similar to the Unix 'tee' command.
103 103
104 104 When the object is closed or deleted, it closes the original file given to
105 105 it for duplication.
106 106 """
107 107 # Inspired by:
108 108 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
109 109
110 110 def __init__(self, file_or_name, mode="w", channel='stdout'):
111 111 """Construct a new Tee object.
112 112
113 113 Parameters
114 114 ----------
115 115 file_or_name : filename or open filehandle (writable)
116 116 File that will be duplicated
117 117
118 118 mode : optional, valid mode for open().
119 119 If a filename was give, open with this mode.
120 120
121 121 channel : str, one of ['stdout', 'stderr']
122 122 """
123 123 if channel not in ['stdout', 'stderr']:
124 124 raise ValueError('Invalid channel spec %s' % channel)
125 125
126 126 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
127 127 self.file = file_or_name
128 128 else:
129 129 self.file = open(file_or_name, mode)
130 130 self.channel = channel
131 131 self.ostream = getattr(sys, channel)
132 132 setattr(sys, channel, self)
133 133 self._closed = False
134 134
135 135 def close(self):
136 136 """Close the file and restore the channel."""
137 137 self.flush()
138 138 setattr(sys, self.channel, self.ostream)
139 139 self.file.close()
140 140 self._closed = True
141 141
142 142 def write(self, data):
143 143 """Write data to both channels."""
144 144 self.file.write(data)
145 145 self.ostream.write(data)
146 146 self.ostream.flush()
147 147
148 148 def flush(self):
149 149 """Flush both channels."""
150 150 self.file.flush()
151 151 self.ostream.flush()
152 152
153 153 def __del__(self):
154 154 if not self._closed:
155 155 self.close()
156 156
157 157
158 def source_to_unicode(txt):
159 """Converts string with python source code to unicode
160 """
161 if isinstance(txt, unicode):
162 return txt
158 def guess_encoding(lines):
159 """check list of lines for line matching the source code encoding pattern
163 160
161 Only check first two lines
162 """
164 163 reg = re.compile("#.*coding[:=]\s*([-\w.]+)")
165 for row in txt.split("\n", 2)[:2]: #We only need to check the first two lines
164 for row in lines[:2]: #We only need to check the first two lines
166 165 result = reg.match(row)
167 166 if result:
168 167 coding = result.groups()[0]
169 168 break
170 169 else:
171 170 coding = "ascii"
171 return coding
172
173 def source_to_unicode(txt):
174 """Converts string with python source code to unicode
175 """
176 if isinstance(txt, unicode):
177 return txt
178 coding = guess_encoding(txt.split("\n", 2))
172 179 return txt.decode(coding, errors="replace")
173 180
174 181
175 182 def file_read(filename):
176 183 """Read a file and close it. Returns the file source."""
177 184 fobj = open(filename,'r');
178 185 source = fobj.read();
179 186 fobj.close()
180 187 return source
181 188
182 189
183 190 def file_readlines(filename):
184 191 """Read a file and close it. Returns the file source using readlines()."""
185 192 fobj = open(filename,'r');
186 193 lines = fobj.readlines();
187 194 fobj.close()
188 195 return lines
189 196
190 197
191 198 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
192 199 """Take multiple lines of input.
193 200
194 201 A list with each line of input as a separate element is returned when a
195 202 termination string is entered (defaults to a single '.'). Input can also
196 203 terminate via EOF (^D in Unix, ^Z-RET in Windows).
197 204
198 205 Lines of input which end in \\ are joined into single entries (and a
199 206 secondary continuation prompt is issued as long as the user terminates
200 207 lines with \\). This allows entering very long strings which are still
201 208 meant to be treated as single entities.
202 209 """
203 210
204 211 try:
205 212 if header:
206 213 header += '\n'
207 214 lines = [raw_input(header + ps1)]
208 215 except EOFError:
209 216 return []
210 217 terminate = [terminate_str]
211 218 try:
212 219 while lines[-1:] != terminate:
213 220 new_line = raw_input(ps1)
214 221 while new_line.endswith('\\'):
215 222 new_line = new_line[:-1] + raw_input(ps2)
216 223 lines.append(new_line)
217 224
218 225 return lines[:-1] # don't return the termination command
219 226 except EOFError:
220 227 print()
221 228 return lines
222 229
223 230
224 231 def raw_input_ext(prompt='', ps2='... '):
225 232 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
226 233
227 234 line = raw_input(prompt)
228 235 while line.endswith('\\'):
229 236 line = line[:-1] + raw_input(ps2)
230 237 return line
231 238
232 239
233 240 def ask_yes_no(prompt,default=None):
234 241 """Asks a question and returns a boolean (y/n) answer.
235 242
236 243 If default is given (one of 'y','n'), it is used if the user input is
237 244 empty. Otherwise the question is repeated until an answer is given.
238 245
239 246 An EOF is treated as the default answer. If there is no default, an
240 247 exception is raised to prevent infinite loops.
241 248
242 249 Valid answers are: y/yes/n/no (match is not case sensitive)."""
243 250
244 251 answers = {'y':True,'n':False,'yes':True,'no':False}
245 252 ans = None
246 253 while ans not in answers.keys():
247 254 try:
248 255 ans = raw_input(prompt+' ').lower()
249 256 if not ans: # response was an empty string
250 257 ans = default
251 258 except KeyboardInterrupt:
252 259 pass
253 260 except EOFError:
254 261 if default in answers.keys():
255 262 ans = default
256 263 print()
257 264 else:
258 265 raise
259 266
260 267 return answers[ans]
261 268
262 269
263 270 class NLprinter:
264 271 """Print an arbitrarily nested list, indicating index numbers.
265 272
266 273 An instance of this class called nlprint is available and callable as a
267 274 function.
268 275
269 276 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
270 277 and using 'sep' to separate the index from the value. """
271 278
272 279 def __init__(self):
273 280 self.depth = 0
274 281
275 282 def __call__(self,lst,pos='',**kw):
276 283 """Prints the nested list numbering levels."""
277 284 kw.setdefault('indent',' ')
278 285 kw.setdefault('sep',': ')
279 286 kw.setdefault('start',0)
280 287 kw.setdefault('stop',len(lst))
281 288 # we need to remove start and stop from kw so they don't propagate
282 289 # into a recursive call for a nested list.
283 290 start = kw['start']; del kw['start']
284 291 stop = kw['stop']; del kw['stop']
285 292 if self.depth == 0 and 'header' in kw.keys():
286 293 print(kw['header'])
287 294
288 295 for idx in range(start,stop):
289 296 elem = lst[idx]
290 297 newpos = pos + str(idx)
291 298 if type(elem)==type([]):
292 299 self.depth += 1
293 300 self.__call__(elem, newpos+",", **kw)
294 301 self.depth -= 1
295 302 else:
296 303 print(kw['indent']*self.depth + newpos + kw["sep"] + repr(elem))
297 304
298 305 nlprint = NLprinter()
299 306
300 307
301 308 def temp_pyfile(src, ext='.py'):
302 309 """Make a temporary python file, return filename and filehandle.
303 310
304 311 Parameters
305 312 ----------
306 313 src : string or list of strings (no need for ending newlines if list)
307 314 Source code to be written to the file.
308 315
309 316 ext : optional, string
310 317 Extension for the generated file.
311 318
312 319 Returns
313 320 -------
314 321 (filename, open filehandle)
315 322 It is the caller's responsibility to close the open file and unlink it.
316 323 """
317 324 fname = tempfile.mkstemp(ext)[1]
318 325 f = open(fname,'w')
319 326 f.write(src)
320 327 f.flush()
321 328 return fname, f
322 329
323 330
324 331 def raw_print(*args, **kw):
325 332 """Raw print to sys.__stdout__, otherwise identical interface to print()."""
326 333
327 334 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
328 335 file=sys.__stdout__)
329 336 sys.__stdout__.flush()
330 337
331 338
332 339 def raw_print_err(*args, **kw):
333 340 """Raw print to sys.__stderr__, otherwise identical interface to print()."""
334 341
335 342 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
336 343 file=sys.__stderr__)
337 344 sys.__stderr__.flush()
338 345
339 346
340 347 # Short aliases for quick debugging, do NOT use these in production code.
341 348 rprint = raw_print
342 349 rprinte = raw_print_err
343 350
344 351
345 352 class CapturedIO(object):
346 353 """Simple object for containing captured stdout/err StringIO objects"""
347 354
348 355 def __init__(self, stdout, stderr):
349 356 self._stdout = stdout
350 357 self._stderr = stderr
351 358
352 359 def __str__(self):
353 360 return self.stdout
354 361
355 362 @property
356 363 def stdout(self):
357 364 if not self._stdout:
358 365 return ''
359 366 return self._stdout.getvalue()
360 367
361 368 @property
362 369 def stderr(self):
363 370 if not self._stderr:
364 371 return ''
365 372 return self._stderr.getvalue()
366 373
367 374 def show(self):
368 375 """write my output to sys.stdout/err as appropriate"""
369 376 sys.stdout.write(self.stdout)
370 377 sys.stderr.write(self.stderr)
371 378 sys.stdout.flush()
372 379 sys.stderr.flush()
373 380
374 381 __call__ = show
375 382
376 383
377 384 class capture_output(object):
378 385 """context manager for capturing stdout/err"""
379 386 stdout = True
380 387 stderr = True
381 388
382 389 def __init__(self, stdout=True, stderr=True):
383 390 self.stdout = stdout
384 391 self.stderr = stderr
385 392
386 393 def __enter__(self):
387 394 self.sys_stdout = sys.stdout
388 395 self.sys_stderr = sys.stderr
389 396
390 397 stdout = stderr = False
391 398 if self.stdout:
392 399 stdout = sys.stdout = StringIO()
393 400 if self.stderr:
394 401 stderr = sys.stderr = StringIO()
395 402
396 403 return CapturedIO(stdout, stderr)
397 404
398 405 def __exit__(self, exc_type, exc_value, traceback):
399 406 sys.stdout = self.sys_stdout
400 407 sys.stderr = self.sys_stderr
401 408
402 409
General Comments 0
You need to be logged in to leave comments. Login now