##// END OF EJS Templates
Use raw string when invalid escape sequence present....
Matthias Bussonnier -
Show More
@@ -1,645 +1,645 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 https://docs.python.org/2/license.html
17 17 """
18 18
19 19 #*****************************************************************************
20 20 #
21 21 # This file is licensed under the PSF license.
22 22 #
23 23 # Copyright (C) 2001 Python Software Foundation, www.python.org
24 24 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
25 25 #
26 26 #
27 27 #*****************************************************************************
28 28
29 29 import bdb
30 30 import functools
31 31 import inspect
32 32 import linecache
33 33 import sys
34 34 import warnings
35 35 import re
36 36
37 37 from IPython import get_ipython
38 38 from IPython.utils import PyColorize
39 39 from IPython.utils import coloransi, py3compat
40 40 from IPython.core.excolors import exception_colors
41 41 from IPython.testing.skipdoctest import skip_doctest
42 42
43 43
44 44 prompt = 'ipdb> '
45 45
46 46 #We have to check this directly from sys.argv, config struct not yet available
47 47 from pdb import Pdb as OldPdb
48 48
49 49 # Allow the set_trace code to operate outside of an ipython instance, even if
50 50 # it does so with some limitations. The rest of this support is implemented in
51 51 # the Tracer constructor.
52 52
53 53 def make_arrow(pad):
54 54 """generate the leading arrow in front of traceback or debugger"""
55 55 if pad >= 2:
56 56 return '-'*(pad-2) + '> '
57 57 elif pad == 1:
58 58 return '>'
59 59 return ''
60 60
61 61
62 62 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
63 63 """Exception hook which handles `BdbQuit` exceptions.
64 64
65 65 All other exceptions are processed using the `excepthook`
66 66 parameter.
67 67 """
68 68 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
69 69 DeprecationWarning, stacklevel=2)
70 70 if et==bdb.BdbQuit:
71 71 print('Exiting Debugger.')
72 72 elif excepthook is not None:
73 73 excepthook(et, ev, tb)
74 74 else:
75 75 # Backwards compatibility. Raise deprecation warning?
76 76 BdbQuit_excepthook.excepthook_ori(et,ev,tb)
77 77
78 78
79 79 def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None):
80 80 warnings.warn(
81 81 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
82 82 DeprecationWarning, stacklevel=2)
83 83 print('Exiting Debugger.')
84 84
85 85
86 86 class Tracer(object):
87 87 """
88 88 DEPRECATED
89 89
90 90 Class for local debugging, similar to pdb.set_trace.
91 91
92 92 Instances of this class, when called, behave like pdb.set_trace, but
93 93 providing IPython's enhanced capabilities.
94 94
95 95 This is implemented as a class which must be initialized in your own code
96 96 and not as a standalone function because we need to detect at runtime
97 97 whether IPython is already active or not. That detection is done in the
98 98 constructor, ensuring that this code plays nicely with a running IPython,
99 99 while functioning acceptably (though with limitations) if outside of it.
100 100 """
101 101
102 102 @skip_doctest
103 103 def __init__(self, colors=None):
104 104 """
105 105 DEPRECATED
106 106
107 107 Create a local debugger instance.
108 108
109 109 Parameters
110 110 ----------
111 111
112 112 colors : str, optional
113 113 The name of the color scheme to use, it must be one of IPython's
114 114 valid color schemes. If not given, the function will default to
115 115 the current IPython scheme when running inside IPython, and to
116 116 'NoColor' otherwise.
117 117
118 118 Examples
119 119 --------
120 120 ::
121 121
122 122 from IPython.core.debugger import Tracer; debug_here = Tracer()
123 123
124 124 Later in your code::
125 125
126 126 debug_here() # -> will open up the debugger at that point.
127 127
128 128 Once the debugger activates, you can use all of its regular commands to
129 129 step through code, set breakpoints, etc. See the pdb documentation
130 130 from the Python standard library for usage details.
131 131 """
132 132 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
133 133 "`IPython.core.debugger.Pdb.set_trace()`",
134 134 DeprecationWarning, stacklevel=2)
135 135
136 136 ip = get_ipython()
137 137 if ip is None:
138 138 # Outside of ipython, we set our own exception hook manually
139 139 sys.excepthook = functools.partial(BdbQuit_excepthook,
140 140 excepthook=sys.excepthook)
141 141 def_colors = 'NoColor'
142 142 else:
143 143 # In ipython, we use its custom exception handler mechanism
144 144 def_colors = ip.colors
145 145 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
146 146
147 147 if colors is None:
148 148 colors = def_colors
149 149
150 150 # The stdlib debugger internally uses a modified repr from the `repr`
151 151 # module, that limits the length of printed strings to a hardcoded
152 152 # limit of 30 characters. That much trimming is too aggressive, let's
153 153 # at least raise that limit to 80 chars, which should be enough for
154 154 # most interactive uses.
155 155 try:
156 156 try:
157 157 from reprlib import aRepr # Py 3
158 158 except ImportError:
159 159 from repr import aRepr # Py 2
160 160 aRepr.maxstring = 80
161 161 except:
162 162 # This is only a user-facing convenience, so any error we encounter
163 163 # here can be warned about but can be otherwise ignored. These
164 164 # printouts will tell us about problems if this API changes
165 165 import traceback
166 166 traceback.print_exc()
167 167
168 168 self.debugger = Pdb(colors)
169 169
170 170 def __call__(self):
171 171 """Starts an interactive debugger at the point where called.
172 172
173 173 This is similar to the pdb.set_trace() function from the std lib, but
174 174 using IPython's enhanced debugger."""
175 175
176 176 self.debugger.set_trace(sys._getframe().f_back)
177 177
178 178
179 RGX_EXTRA_INDENT = re.compile('(?<=\n)\s+')
179 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
180 180
181 181
182 182 def strip_indentation(multiline_string):
183 183 return RGX_EXTRA_INDENT.sub('', multiline_string)
184 184
185 185
186 186 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
187 187 """Make new_fn have old_fn's doc string. This is particularly useful
188 188 for the ``do_...`` commands that hook into the help system.
189 189 Adapted from from a comp.lang.python posting
190 190 by Duncan Booth."""
191 191 def wrapper(*args, **kw):
192 192 return new_fn(*args, **kw)
193 193 if old_fn.__doc__:
194 194 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
195 195 return wrapper
196 196
197 197
198 198 def _file_lines(fname):
199 199 """Return the contents of a named file as a list of lines.
200 200
201 201 This function never raises an IOError exception: if the file can't be
202 202 read, it simply returns an empty list."""
203 203
204 204 try:
205 205 outfile = open(fname)
206 206 except IOError:
207 207 return []
208 208 else:
209 209 out = outfile.readlines()
210 210 outfile.close()
211 211 return out
212 212
213 213
214 214 class Pdb(OldPdb):
215 215 """Modified Pdb class, does not load readline.
216 216
217 217 for a standalone version that uses prompt_toolkit, see
218 218 `IPython.terminal.debugger.TerminalPdb` and
219 219 `IPython.terminal.debugger.set_trace()`
220 220 """
221 221
222 222 def __init__(self, color_scheme=None, completekey=None,
223 223 stdin=None, stdout=None, context=5):
224 224
225 225 # Parent constructor:
226 226 try:
227 227 self.context = int(context)
228 228 if self.context <= 0:
229 229 raise ValueError("Context must be a positive integer")
230 230 except (TypeError, ValueError):
231 231 raise ValueError("Context must be a positive integer")
232 232
233 233 OldPdb.__init__(self, completekey, stdin, stdout)
234 234
235 235 # IPython changes...
236 236 self.shell = get_ipython()
237 237
238 238 if self.shell is None:
239 239 save_main = sys.modules['__main__']
240 240 # No IPython instance running, we must create one
241 241 from IPython.terminal.interactiveshell import \
242 242 TerminalInteractiveShell
243 243 self.shell = TerminalInteractiveShell.instance()
244 244 # needed by any code which calls __import__("__main__") after
245 245 # the debugger was entered. See also #9941.
246 246 sys.modules['__main__'] = save_main
247 247
248 248 if color_scheme is not None:
249 249 warnings.warn(
250 250 "The `color_scheme` argument is deprecated since version 5.1",
251 251 DeprecationWarning, stacklevel=2)
252 252 else:
253 253 color_scheme = self.shell.colors
254 254
255 255 self.aliases = {}
256 256
257 257 # Create color table: we copy the default one from the traceback
258 258 # module and add a few attributes needed for debugging
259 259 self.color_scheme_table = exception_colors()
260 260
261 261 # shorthands
262 262 C = coloransi.TermColors
263 263 cst = self.color_scheme_table
264 264
265 265 cst['NoColor'].colors.prompt = C.NoColor
266 266 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
267 267 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
268 268
269 269 cst['Linux'].colors.prompt = C.Green
270 270 cst['Linux'].colors.breakpoint_enabled = C.LightRed
271 271 cst['Linux'].colors.breakpoint_disabled = C.Red
272 272
273 273 cst['LightBG'].colors.prompt = C.Blue
274 274 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
275 275 cst['LightBG'].colors.breakpoint_disabled = C.Red
276 276
277 277 cst['Neutral'].colors.prompt = C.Blue
278 278 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
279 279 cst['Neutral'].colors.breakpoint_disabled = C.Red
280 280
281 281
282 282 # Add a python parser so we can syntax highlight source while
283 283 # debugging.
284 284 self.parser = PyColorize.Parser(style=color_scheme)
285 285 self.set_colors(color_scheme)
286 286
287 287 # Set the prompt - the default prompt is '(Pdb)'
288 288 self.prompt = prompt
289 289
290 290 def set_colors(self, scheme):
291 291 """Shorthand access to the color table scheme selector method."""
292 292 self.color_scheme_table.set_active_scheme(scheme)
293 293 self.parser.style = scheme
294 294
295 295 def interaction(self, frame, traceback):
296 296 try:
297 297 OldPdb.interaction(self, frame, traceback)
298 298 except KeyboardInterrupt:
299 299 sys.stdout.write('\n' + self.shell.get_exception_only())
300 300
301 301 def new_do_up(self, arg):
302 302 OldPdb.do_up(self, arg)
303 303 do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up)
304 304
305 305 def new_do_down(self, arg):
306 306 OldPdb.do_down(self, arg)
307 307
308 308 do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down)
309 309
310 310 def new_do_frame(self, arg):
311 311 OldPdb.do_frame(self, arg)
312 312
313 313 def new_do_quit(self, arg):
314 314
315 315 if hasattr(self, 'old_all_completions'):
316 316 self.shell.Completer.all_completions=self.old_all_completions
317 317
318 318 return OldPdb.do_quit(self, arg)
319 319
320 320 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
321 321
322 322 def new_do_restart(self, arg):
323 323 """Restart command. In the context of ipython this is exactly the same
324 324 thing as 'quit'."""
325 325 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
326 326 return self.do_quit(arg)
327 327
328 328 def print_stack_trace(self, context=None):
329 329 if context is None:
330 330 context = self.context
331 331 try:
332 332 context=int(context)
333 333 if context <= 0:
334 334 raise ValueError("Context must be a positive integer")
335 335 except (TypeError, ValueError):
336 336 raise ValueError("Context must be a positive integer")
337 337 try:
338 338 for frame_lineno in self.stack:
339 339 self.print_stack_entry(frame_lineno, context=context)
340 340 except KeyboardInterrupt:
341 341 pass
342 342
343 343 def print_stack_entry(self,frame_lineno, prompt_prefix='\n-> ',
344 344 context=None):
345 345 if context is None:
346 346 context = self.context
347 347 try:
348 348 context=int(context)
349 349 if context <= 0:
350 350 raise ValueError("Context must be a positive integer")
351 351 except (TypeError, ValueError):
352 352 raise ValueError("Context must be a positive integer")
353 353 print(self.format_stack_entry(frame_lineno, '', context))
354 354
355 355 # vds: >>
356 356 frame, lineno = frame_lineno
357 357 filename = frame.f_code.co_filename
358 358 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
359 359 # vds: <<
360 360
361 361 def format_stack_entry(self, frame_lineno, lprefix=': ', context=None):
362 362 if context is None:
363 363 context = self.context
364 364 try:
365 365 context=int(context)
366 366 if context <= 0:
367 367 print("Context must be a positive integer")
368 368 except (TypeError, ValueError):
369 369 print("Context must be a positive integer")
370 370 try:
371 371 import reprlib # Py 3
372 372 except ImportError:
373 373 import repr as reprlib # Py 2
374 374
375 375 ret = []
376 376
377 377 Colors = self.color_scheme_table.active_colors
378 378 ColorsNormal = Colors.Normal
379 379 tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal)
380 380 tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal)
381 381 tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
382 382 tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line,
383 383 ColorsNormal)
384 384
385 385 frame, lineno = frame_lineno
386 386
387 387 return_value = ''
388 388 if '__return__' in frame.f_locals:
389 389 rv = frame.f_locals['__return__']
390 390 #return_value += '->'
391 391 return_value += reprlib.repr(rv) + '\n'
392 392 ret.append(return_value)
393 393
394 394 #s = filename + '(' + `lineno` + ')'
395 395 filename = self.canonic(frame.f_code.co_filename)
396 396 link = tpl_link % py3compat.cast_unicode(filename)
397 397
398 398 if frame.f_code.co_name:
399 399 func = frame.f_code.co_name
400 400 else:
401 401 func = "<lambda>"
402 402
403 403 call = ''
404 404 if func != '?':
405 405 if '__args__' in frame.f_locals:
406 406 args = reprlib.repr(frame.f_locals['__args__'])
407 407 else:
408 408 args = '()'
409 409 call = tpl_call % (func, args)
410 410
411 411 # The level info should be generated in the same format pdb uses, to
412 412 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
413 413 if frame is self.curframe:
414 414 ret.append('> ')
415 415 else:
416 416 ret.append(' ')
417 417 ret.append(u'%s(%s)%s\n' % (link,lineno,call))
418 418
419 419 start = lineno - 1 - context//2
420 420 lines = linecache.getlines(filename)
421 421 start = min(start, len(lines) - context)
422 422 start = max(start, 0)
423 423 lines = lines[start : start + context]
424 424
425 425 for i,line in enumerate(lines):
426 426 show_arrow = (start + 1 + i == lineno)
427 427 linetpl = (frame is self.curframe or show_arrow) \
428 428 and tpl_line_em \
429 429 or tpl_line
430 430 ret.append(self.__format_line(linetpl, filename,
431 431 start + 1 + i, line,
432 432 arrow = show_arrow) )
433 433 return ''.join(ret)
434 434
435 435 def __format_line(self, tpl_line, filename, lineno, line, arrow = False):
436 436 bp_mark = ""
437 437 bp_mark_color = ""
438 438
439 439 new_line, err = self.parser.format2(line, 'str')
440 440 if not err:
441 441 line = new_line
442 442
443 443 bp = None
444 444 if lineno in self.get_file_breaks(filename):
445 445 bps = self.get_breaks(filename, lineno)
446 446 bp = bps[-1]
447 447
448 448 if bp:
449 449 Colors = self.color_scheme_table.active_colors
450 450 bp_mark = str(bp.number)
451 451 bp_mark_color = Colors.breakpoint_enabled
452 452 if not bp.enabled:
453 453 bp_mark_color = Colors.breakpoint_disabled
454 454
455 455 numbers_width = 7
456 456 if arrow:
457 457 # This is the line with the error
458 458 pad = numbers_width - len(str(lineno)) - len(bp_mark)
459 459 num = '%s%s' % (make_arrow(pad), str(lineno))
460 460 else:
461 461 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
462 462
463 463 return tpl_line % (bp_mark_color + bp_mark, num, line)
464 464
465 465
466 466 def print_list_lines(self, filename, first, last):
467 467 """The printing (as opposed to the parsing part of a 'list'
468 468 command."""
469 469 try:
470 470 Colors = self.color_scheme_table.active_colors
471 471 ColorsNormal = Colors.Normal
472 472 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
473 473 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
474 474 src = []
475 475 if filename == "<string>" and hasattr(self, "_exec_filename"):
476 476 filename = self._exec_filename
477 477
478 478 for lineno in range(first, last+1):
479 479 line = linecache.getline(filename, lineno)
480 480 if not line:
481 481 break
482 482
483 483 if lineno == self.curframe.f_lineno:
484 484 line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True)
485 485 else:
486 486 line = self.__format_line(tpl_line, filename, lineno, line, arrow = False)
487 487
488 488 src.append(line)
489 489 self.lineno = lineno
490 490
491 491 print(''.join(src))
492 492
493 493 except KeyboardInterrupt:
494 494 pass
495 495
496 496 def do_list(self, arg):
497 497 """Print lines of code from the current stack frame
498 498 """
499 499 self.lastcmd = 'list'
500 500 last = None
501 501 if arg:
502 502 try:
503 503 x = eval(arg, {}, {})
504 504 if type(x) == type(()):
505 505 first, last = x
506 506 first = int(first)
507 507 last = int(last)
508 508 if last < first:
509 509 # Assume it's a count
510 510 last = first + last
511 511 else:
512 512 first = max(1, int(x) - 5)
513 513 except:
514 514 print('*** Error in argument:', repr(arg))
515 515 return
516 516 elif self.lineno is None:
517 517 first = max(1, self.curframe.f_lineno - 5)
518 518 else:
519 519 first = self.lineno + 1
520 520 if last is None:
521 521 last = first + 10
522 522 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
523 523
524 524 # vds: >>
525 525 lineno = first
526 526 filename = self.curframe.f_code.co_filename
527 527 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
528 528 # vds: <<
529 529
530 530 do_l = do_list
531 531
532 532 def getsourcelines(self, obj):
533 533 lines, lineno = inspect.findsource(obj)
534 534 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
535 535 # must be a module frame: do not try to cut a block out of it
536 536 return lines, 1
537 537 elif inspect.ismodule(obj):
538 538 return lines, 1
539 539 return inspect.getblock(lines[lineno:]), lineno+1
540 540
541 541 def do_longlist(self, arg):
542 542 """Print lines of code from the current stack frame.
543 543
544 544 Shows more lines than 'list' does.
545 545 """
546 546 self.lastcmd = 'longlist'
547 547 try:
548 548 lines, lineno = self.getsourcelines(self.curframe)
549 549 except OSError as err:
550 550 self.error(err)
551 551 return
552 552 last = lineno + len(lines)
553 553 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
554 554 do_ll = do_longlist
555 555
556 556 def do_debug(self, arg):
557 557 """debug code
558 558 Enter a recursive debugger that steps through the code
559 559 argument (which is an arbitrary expression or statement to be
560 560 executed in the current environment).
561 561 """
562 562 sys.settrace(None)
563 563 globals = self.curframe.f_globals
564 564 locals = self.curframe_locals
565 565 p = self.__class__(completekey=self.completekey,
566 566 stdin=self.stdin, stdout=self.stdout)
567 567 p.use_rawinput = self.use_rawinput
568 568 p.prompt = "(%s) " % self.prompt.strip()
569 569 self.message("ENTERING RECURSIVE DEBUGGER")
570 570 sys.call_tracing(p.run, (arg, globals, locals))
571 571 self.message("LEAVING RECURSIVE DEBUGGER")
572 572 sys.settrace(self.trace_dispatch)
573 573 self.lastcmd = p.lastcmd
574 574
575 575 def do_pdef(self, arg):
576 576 """Print the call signature for any callable object.
577 577
578 578 The debugger interface to %pdef"""
579 579 namespaces = [('Locals', self.curframe.f_locals),
580 580 ('Globals', self.curframe.f_globals)]
581 581 self.shell.find_line_magic('pdef')(arg, namespaces=namespaces)
582 582
583 583 def do_pdoc(self, arg):
584 584 """Print the docstring for an object.
585 585
586 586 The debugger interface to %pdoc."""
587 587 namespaces = [('Locals', self.curframe.f_locals),
588 588 ('Globals', self.curframe.f_globals)]
589 589 self.shell.find_line_magic('pdoc')(arg, namespaces=namespaces)
590 590
591 591 def do_pfile(self, arg):
592 592 """Print (or run through pager) the file where an object is defined.
593 593
594 594 The debugger interface to %pfile.
595 595 """
596 596 namespaces = [('Locals', self.curframe.f_locals),
597 597 ('Globals', self.curframe.f_globals)]
598 598 self.shell.find_line_magic('pfile')(arg, namespaces=namespaces)
599 599
600 600 def do_pinfo(self, arg):
601 601 """Provide detailed information about an object.
602 602
603 603 The debugger interface to %pinfo, i.e., obj?."""
604 604 namespaces = [('Locals', self.curframe.f_locals),
605 605 ('Globals', self.curframe.f_globals)]
606 606 self.shell.find_line_magic('pinfo')(arg, namespaces=namespaces)
607 607
608 608 def do_pinfo2(self, arg):
609 609 """Provide extra detailed information about an object.
610 610
611 611 The debugger interface to %pinfo2, i.e., obj??."""
612 612 namespaces = [('Locals', self.curframe.f_locals),
613 613 ('Globals', self.curframe.f_globals)]
614 614 self.shell.find_line_magic('pinfo2')(arg, namespaces=namespaces)
615 615
616 616 def do_psource(self, arg):
617 617 """Print (or run through pager) the source code for an object."""
618 618 namespaces = [('Locals', self.curframe.f_locals),
619 619 ('Globals', self.curframe.f_globals)]
620 620 self.shell.find_line_magic('psource')(arg, namespaces=namespaces)
621 621
622 622 def do_where(self, arg):
623 623 """w(here)
624 624 Print a stack trace, with the most recent frame at the bottom.
625 625 An arrow indicates the "current frame", which determines the
626 626 context of most commands. 'bt' is an alias for this command.
627 627
628 628 Take a number as argument as an (optional) number of context line to
629 629 print"""
630 630 if arg:
631 631 context = int(arg)
632 632 self.print_stack_trace(context)
633 633 else:
634 634 self.print_stack_trace()
635 635
636 636 do_w = do_where
637 637
638 638
639 639 def set_trace(frame=None):
640 640 """
641 641 Start debugging from `frame`.
642 642
643 643 If frame is not specified, debugging starts from caller's frame.
644 644 """
645 645 Pdb().set_trace(frame or sys._getframe().f_back)
@@ -1,438 +1,438 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for path handling.
4 4 """
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import os
10 10 import sys
11 11 import errno
12 12 import shutil
13 13 import random
14 14 import glob
15 15 from warnings import warn
16 16
17 17 from IPython.utils.process import system
18 18 from IPython.utils import py3compat
19 19 from IPython.utils.decorators import undoc
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Code
23 23 #-----------------------------------------------------------------------------
24 24
25 25 fs_encoding = sys.getfilesystemencoding()
26 26
27 27 def _writable_dir(path):
28 28 """Whether `path` is a directory, to which the user has write access."""
29 29 return os.path.isdir(path) and os.access(path, os.W_OK)
30 30
31 31 if sys.platform == 'win32':
32 32 def _get_long_path_name(path):
33 33 """Get a long path name (expand ~) on Windows using ctypes.
34 34
35 35 Examples
36 36 --------
37 37
38 38 >>> get_long_path_name('c:\\docume~1')
39 39 'c:\\\\Documents and Settings'
40 40
41 41 """
42 42 try:
43 43 import ctypes
44 44 except ImportError:
45 45 raise ImportError('you need to have ctypes installed for this to work')
46 46 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
47 47 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
48 48 ctypes.c_uint ]
49 49
50 50 buf = ctypes.create_unicode_buffer(260)
51 51 rv = _GetLongPathName(path, buf, 260)
52 52 if rv == 0 or rv > 260:
53 53 return path
54 54 else:
55 55 return buf.value
56 56 else:
57 57 def _get_long_path_name(path):
58 58 """Dummy no-op."""
59 59 return path
60 60
61 61
62 62
63 63 def get_long_path_name(path):
64 64 """Expand a path into its long form.
65 65
66 66 On Windows this expands any ~ in the paths. On other platforms, it is
67 67 a null operation.
68 68 """
69 69 return _get_long_path_name(path)
70 70
71 71
72 72 def unquote_filename(name, win32=(sys.platform=='win32')):
73 73 """ On Windows, remove leading and trailing quotes from filenames.
74 74
75 75 This function has been deprecated and should not be used any more:
76 76 unquoting is now taken care of by :func:`IPython.utils.process.arg_split`.
77 77 """
78 78 warn("'unquote_filename' is deprecated since IPython 5.0 and should not "
79 79 "be used anymore", DeprecationWarning, stacklevel=2)
80 80 if win32:
81 81 if name.startswith(("'", '"')) and name.endswith(("'", '"')):
82 82 name = name[1:-1]
83 83 return name
84 84
85 85
86 86 def compress_user(path):
87 87 """Reverse of :func:`os.path.expanduser`
88 88 """
89 89 home = os.path.expanduser('~')
90 90 if path.startswith(home):
91 91 path = "~" + path[len(home):]
92 92 return path
93 93
94 94 def get_py_filename(name, force_win32=None):
95 95 """Return a valid python filename in the current directory.
96 96
97 97 If the given name is not a file, it adds '.py' and searches again.
98 98 Raises IOError with an informative message if the file isn't found.
99 99 """
100 100
101 101 name = os.path.expanduser(name)
102 102 if force_win32 is not None:
103 103 warn("The 'force_win32' argument to 'get_py_filename' is deprecated "
104 104 "since IPython 5.0 and should not be used anymore",
105 105 DeprecationWarning, stacklevel=2)
106 106 if not os.path.isfile(name) and not name.endswith('.py'):
107 107 name += '.py'
108 108 if os.path.isfile(name):
109 109 return name
110 110 else:
111 111 raise IOError('File `%r` not found.' % name)
112 112
113 113
114 114 def filefind(filename, path_dirs=None):
115 115 """Find a file by looking through a sequence of paths.
116 116
117 117 This iterates through a sequence of paths looking for a file and returns
118 118 the full, absolute path of the first occurrence of the file. If no set of
119 119 path dirs is given, the filename is tested as is, after running through
120 120 :func:`expandvars` and :func:`expanduser`. Thus a simple call::
121 121
122 122 filefind('myfile.txt')
123 123
124 124 will find the file in the current working dir, but::
125 125
126 126 filefind('~/myfile.txt')
127 127
128 128 Will find the file in the users home directory. This function does not
129 129 automatically try any paths, such as the cwd or the user's home directory.
130 130
131 131 Parameters
132 132 ----------
133 133 filename : str
134 134 The filename to look for.
135 135 path_dirs : str, None or sequence of str
136 136 The sequence of paths to look for the file in. If None, the filename
137 137 need to be absolute or be in the cwd. If a string, the string is
138 138 put into a sequence and the searched. If a sequence, walk through
139 139 each element and join with ``filename``, calling :func:`expandvars`
140 140 and :func:`expanduser` before testing for existence.
141 141
142 142 Returns
143 143 -------
144 144 Raises :exc:`IOError` or returns absolute path to file.
145 145 """
146 146
147 147 # If paths are quoted, abspath gets confused, strip them...
148 148 filename = filename.strip('"').strip("'")
149 149 # If the input is an absolute path, just check it exists
150 150 if os.path.isabs(filename) and os.path.isfile(filename):
151 151 return filename
152 152
153 153 if path_dirs is None:
154 154 path_dirs = ("",)
155 155 elif isinstance(path_dirs, str):
156 156 path_dirs = (path_dirs,)
157 157
158 158 for path in path_dirs:
159 159 if path == '.': path = os.getcwd()
160 160 testname = expand_path(os.path.join(path, filename))
161 161 if os.path.isfile(testname):
162 162 return os.path.abspath(testname)
163 163
164 164 raise IOError("File %r does not exist in any of the search paths: %r" %
165 165 (filename, path_dirs) )
166 166
167 167
168 168 class HomeDirError(Exception):
169 169 pass
170 170
171 171
172 172 def get_home_dir(require_writable=False):
173 173 """Return the 'home' directory, as a unicode string.
174 174
175 175 Uses os.path.expanduser('~'), and checks for writability.
176 176
177 177 See stdlib docs for how this is determined.
178 178 $HOME is first priority on *ALL* platforms.
179 179
180 180 Parameters
181 181 ----------
182 182
183 183 require_writable : bool [default: False]
184 184 if True:
185 185 guarantees the return value is a writable directory, otherwise
186 186 raises HomeDirError
187 187 if False:
188 188 The path is resolved, but it is not guaranteed to exist or be writable.
189 189 """
190 190
191 191 homedir = os.path.expanduser('~')
192 192 # Next line will make things work even when /home/ is a symlink to
193 193 # /usr/home as it is on FreeBSD, for example
194 194 homedir = os.path.realpath(homedir)
195 195
196 196 if not _writable_dir(homedir) and os.name == 'nt':
197 197 # expanduser failed, use the registry to get the 'My Documents' folder.
198 198 try:
199 199 try:
200 200 import winreg as wreg # Py 3
201 201 except ImportError:
202 202 import _winreg as wreg # Py 2
203 203 key = wreg.OpenKey(
204 204 wreg.HKEY_CURRENT_USER,
205 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
205 r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
206 206 )
207 207 homedir = wreg.QueryValueEx(key,'Personal')[0]
208 208 key.Close()
209 209 except:
210 210 pass
211 211
212 212 if (not require_writable) or _writable_dir(homedir):
213 213 return py3compat.cast_unicode(homedir, fs_encoding)
214 214 else:
215 215 raise HomeDirError('%s is not a writable dir, '
216 216 'set $HOME environment variable to override' % homedir)
217 217
218 218 def get_xdg_dir():
219 219 """Return the XDG_CONFIG_HOME, if it is defined and exists, else None.
220 220
221 221 This is only for non-OS X posix (Linux,Unix,etc.) systems.
222 222 """
223 223
224 224 env = os.environ
225 225
226 226 if os.name == 'posix' and sys.platform != 'darwin':
227 227 # Linux, Unix, AIX, etc.
228 228 # use ~/.config if empty OR not set
229 229 xdg = env.get("XDG_CONFIG_HOME", None) or os.path.join(get_home_dir(), '.config')
230 230 if xdg and _writable_dir(xdg):
231 231 return py3compat.cast_unicode(xdg, fs_encoding)
232 232
233 233 return None
234 234
235 235
236 236 def get_xdg_cache_dir():
237 237 """Return the XDG_CACHE_HOME, if it is defined and exists, else None.
238 238
239 239 This is only for non-OS X posix (Linux,Unix,etc.) systems.
240 240 """
241 241
242 242 env = os.environ
243 243
244 244 if os.name == 'posix' and sys.platform != 'darwin':
245 245 # Linux, Unix, AIX, etc.
246 246 # use ~/.cache if empty OR not set
247 247 xdg = env.get("XDG_CACHE_HOME", None) or os.path.join(get_home_dir(), '.cache')
248 248 if xdg and _writable_dir(xdg):
249 249 return py3compat.cast_unicode(xdg, fs_encoding)
250 250
251 251 return None
252 252
253 253
254 254 @undoc
255 255 def get_ipython_dir():
256 256 warn("get_ipython_dir has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
257 257 from IPython.paths import get_ipython_dir
258 258 return get_ipython_dir()
259 259
260 260 @undoc
261 261 def get_ipython_cache_dir():
262 262 warn("get_ipython_cache_dir has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
263 263 from IPython.paths import get_ipython_cache_dir
264 264 return get_ipython_cache_dir()
265 265
266 266 @undoc
267 267 def get_ipython_package_dir():
268 268 warn("get_ipython_package_dir has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
269 269 from IPython.paths import get_ipython_package_dir
270 270 return get_ipython_package_dir()
271 271
272 272 @undoc
273 273 def get_ipython_module_path(module_str):
274 274 warn("get_ipython_module_path has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
275 275 from IPython.paths import get_ipython_module_path
276 276 return get_ipython_module_path(module_str)
277 277
278 278 @undoc
279 279 def locate_profile(profile='default'):
280 280 warn("locate_profile has moved to the IPython.paths module since IPython 4.0.", stacklevel=2)
281 281 from IPython.paths import locate_profile
282 282 return locate_profile(profile=profile)
283 283
284 284 def expand_path(s):
285 285 """Expand $VARS and ~names in a string, like a shell
286 286
287 287 :Examples:
288 288
289 289 In [2]: os.environ['FOO']='test'
290 290
291 291 In [3]: expand_path('variable FOO is $FOO')
292 292 Out[3]: 'variable FOO is test'
293 293 """
294 294 # This is a pretty subtle hack. When expand user is given a UNC path
295 295 # on Windows (\\server\share$\%username%), os.path.expandvars, removes
296 296 # the $ to get (\\server\share\%username%). I think it considered $
297 297 # alone an empty var. But, we need the $ to remains there (it indicates
298 298 # a hidden share).
299 299 if os.name=='nt':
300 300 s = s.replace('$\\', 'IPYTHON_TEMP')
301 301 s = os.path.expandvars(os.path.expanduser(s))
302 302 if os.name=='nt':
303 303 s = s.replace('IPYTHON_TEMP', '$\\')
304 304 return s
305 305
306 306
307 307 def unescape_glob(string):
308 308 """Unescape glob pattern in `string`."""
309 309 def unescape(s):
310 310 for pattern in '*[]!?':
311 311 s = s.replace(r'\{0}'.format(pattern), pattern)
312 312 return s
313 313 return '\\'.join(map(unescape, string.split('\\\\')))
314 314
315 315
316 316 def shellglob(args):
317 317 """
318 318 Do glob expansion for each element in `args` and return a flattened list.
319 319
320 320 Unmatched glob pattern will remain as-is in the returned list.
321 321
322 322 """
323 323 expanded = []
324 324 # Do not unescape backslash in Windows as it is interpreted as
325 325 # path separator:
326 326 unescape = unescape_glob if sys.platform != 'win32' else lambda x: x
327 327 for a in args:
328 328 expanded.extend(glob.glob(a) or [unescape(a)])
329 329 return expanded
330 330
331 331
332 332 def target_outdated(target,deps):
333 333 """Determine whether a target is out of date.
334 334
335 335 target_outdated(target,deps) -> 1/0
336 336
337 337 deps: list of filenames which MUST exist.
338 338 target: single filename which may or may not exist.
339 339
340 340 If target doesn't exist or is older than any file listed in deps, return
341 341 true, otherwise return false.
342 342 """
343 343 try:
344 344 target_time = os.path.getmtime(target)
345 345 except os.error:
346 346 return 1
347 347 for dep in deps:
348 348 dep_time = os.path.getmtime(dep)
349 349 if dep_time > target_time:
350 350 #print "For target",target,"Dep failed:",dep # dbg
351 351 #print "times (dep,tar):",dep_time,target_time # dbg
352 352 return 1
353 353 return 0
354 354
355 355
356 356 def target_update(target,deps,cmd):
357 357 """Update a target with a given command given a list of dependencies.
358 358
359 359 target_update(target,deps,cmd) -> runs cmd if target is outdated.
360 360
361 361 This is just a wrapper around target_outdated() which calls the given
362 362 command if target is outdated."""
363 363
364 364 if target_outdated(target,deps):
365 365 system(cmd)
366 366
367 367
368 368 ENOLINK = 1998
369 369
370 370 def link(src, dst):
371 371 """Hard links ``src`` to ``dst``, returning 0 or errno.
372 372
373 373 Note that the special errno ``ENOLINK`` will be returned if ``os.link`` isn't
374 374 supported by the operating system.
375 375 """
376 376
377 377 if not hasattr(os, "link"):
378 378 return ENOLINK
379 379 link_errno = 0
380 380 try:
381 381 os.link(src, dst)
382 382 except OSError as e:
383 383 link_errno = e.errno
384 384 return link_errno
385 385
386 386
387 387 def link_or_copy(src, dst):
388 388 """Attempts to hardlink ``src`` to ``dst``, copying if the link fails.
389 389
390 390 Attempts to maintain the semantics of ``shutil.copy``.
391 391
392 392 Because ``os.link`` does not overwrite files, a unique temporary file
393 393 will be used if the target already exists, then that file will be moved
394 394 into place.
395 395 """
396 396
397 397 if os.path.isdir(dst):
398 398 dst = os.path.join(dst, os.path.basename(src))
399 399
400 400 link_errno = link(src, dst)
401 401 if link_errno == errno.EEXIST:
402 402 if os.stat(src).st_ino == os.stat(dst).st_ino:
403 403 # dst is already a hard link to the correct file, so we don't need
404 404 # to do anything else. If we try to link and rename the file
405 405 # anyway, we get duplicate files - see http://bugs.python.org/issue21876
406 406 return
407 407
408 408 new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), )
409 409 try:
410 410 link_or_copy(src, new_dst)
411 411 except:
412 412 try:
413 413 os.remove(new_dst)
414 414 except OSError:
415 415 pass
416 416 raise
417 417 os.rename(new_dst, dst)
418 418 elif link_errno != 0:
419 419 # Either link isn't supported, or the filesystem doesn't support
420 420 # linking, or 'src' and 'dst' are on different filesystems.
421 421 shutil.copy(src, dst)
422 422
423 423 def ensure_dir_exists(path, mode=0o755):
424 424 """ensure that a directory exists
425 425
426 426 If it doesn't exist, try to create it and protect against a race condition
427 427 if another process is doing the same.
428 428
429 429 The default permissions are 755, which differ from os.makedirs default of 777.
430 430 """
431 431 if not os.path.exists(path):
432 432 try:
433 433 os.makedirs(path, mode=mode)
434 434 except OSError as e:
435 435 if e.errno != errno.EEXIST:
436 436 raise
437 437 elif not os.path.isdir(path):
438 438 raise IOError("%r exists but is not a directory" % path)
@@ -1,772 +1,772 b''
1 1 # encoding: utf-8
2 2 """
3 3 Utilities for working with strings and text.
4 4
5 5 Inheritance diagram:
6 6
7 7 .. inheritance-diagram:: IPython.utils.text
8 8 :parts: 3
9 9 """
10 10
11 11 import os
12 12 import re
13 13 import sys
14 14 import textwrap
15 15 from string import Formatter
16 16 from pathlib import Path
17 17
18 18 from IPython.utils import py3compat
19 19
20 20 # datetime.strftime date format for ipython
21 21 if sys.platform == 'win32':
22 22 date_format = "%B %d, %Y"
23 23 else:
24 24 date_format = "%B %-d, %Y"
25 25
26 26 class LSString(str):
27 27 """String derivative with a special access attributes.
28 28
29 29 These are normal strings, but with the special attributes:
30 30
31 31 .l (or .list) : value as list (split on newlines).
32 32 .n (or .nlstr): original value (the string itself).
33 33 .s (or .spstr): value as whitespace-separated string.
34 34 .p (or .paths): list of path objects (requires path.py package)
35 35
36 36 Any values which require transformations are computed only once and
37 37 cached.
38 38
39 39 Such strings are very useful to efficiently interact with the shell, which
40 40 typically only understands whitespace-separated options for commands."""
41 41
42 42 def get_list(self):
43 43 try:
44 44 return self.__list
45 45 except AttributeError:
46 46 self.__list = self.split('\n')
47 47 return self.__list
48 48
49 49 l = list = property(get_list)
50 50
51 51 def get_spstr(self):
52 52 try:
53 53 return self.__spstr
54 54 except AttributeError:
55 55 self.__spstr = self.replace('\n',' ')
56 56 return self.__spstr
57 57
58 58 s = spstr = property(get_spstr)
59 59
60 60 def get_nlstr(self):
61 61 return self
62 62
63 63 n = nlstr = property(get_nlstr)
64 64
65 65 def get_paths(self):
66 66 try:
67 67 return self.__paths
68 68 except AttributeError:
69 69 self.__paths = [Path(p) for p in self.split('\n') if os.path.exists(p)]
70 70 return self.__paths
71 71
72 72 p = paths = property(get_paths)
73 73
74 74 # FIXME: We need to reimplement type specific displayhook and then add this
75 75 # back as a custom printer. This should also be moved outside utils into the
76 76 # core.
77 77
78 78 # def print_lsstring(arg):
79 79 # """ Prettier (non-repr-like) and more informative printer for LSString """
80 80 # print "LSString (.p, .n, .l, .s available). Value:"
81 81 # print arg
82 82 #
83 83 #
84 84 # print_lsstring = result_display.when_type(LSString)(print_lsstring)
85 85
86 86
87 87 class SList(list):
88 88 """List derivative with a special access attributes.
89 89
90 90 These are normal lists, but with the special attributes:
91 91
92 92 * .l (or .list) : value as list (the list itself).
93 93 * .n (or .nlstr): value as a string, joined on newlines.
94 94 * .s (or .spstr): value as a string, joined on spaces.
95 95 * .p (or .paths): list of path objects (requires path.py package)
96 96
97 97 Any values which require transformations are computed only once and
98 98 cached."""
99 99
100 100 def get_list(self):
101 101 return self
102 102
103 103 l = list = property(get_list)
104 104
105 105 def get_spstr(self):
106 106 try:
107 107 return self.__spstr
108 108 except AttributeError:
109 109 self.__spstr = ' '.join(self)
110 110 return self.__spstr
111 111
112 112 s = spstr = property(get_spstr)
113 113
114 114 def get_nlstr(self):
115 115 try:
116 116 return self.__nlstr
117 117 except AttributeError:
118 118 self.__nlstr = '\n'.join(self)
119 119 return self.__nlstr
120 120
121 121 n = nlstr = property(get_nlstr)
122 122
123 123 def get_paths(self):
124 124 try:
125 125 return self.__paths
126 126 except AttributeError:
127 127 self.__paths = [Path(p) for p in self if os.path.exists(p)]
128 128 return self.__paths
129 129
130 130 p = paths = property(get_paths)
131 131
132 132 def grep(self, pattern, prune = False, field = None):
133 133 """ Return all strings matching 'pattern' (a regex or callable)
134 134
135 135 This is case-insensitive. If prune is true, return all items
136 136 NOT matching the pattern.
137 137
138 138 If field is specified, the match must occur in the specified
139 139 whitespace-separated field.
140 140
141 141 Examples::
142 142
143 143 a.grep( lambda x: x.startswith('C') )
144 144 a.grep('Cha.*log', prune=1)
145 145 a.grep('chm', field=-1)
146 146 """
147 147
148 148 def match_target(s):
149 149 if field is None:
150 150 return s
151 151 parts = s.split()
152 152 try:
153 153 tgt = parts[field]
154 154 return tgt
155 155 except IndexError:
156 156 return ""
157 157
158 158 if isinstance(pattern, str):
159 159 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
160 160 else:
161 161 pred = pattern
162 162 if not prune:
163 163 return SList([el for el in self if pred(match_target(el))])
164 164 else:
165 165 return SList([el for el in self if not pred(match_target(el))])
166 166
167 167 def fields(self, *fields):
168 168 """ Collect whitespace-separated fields from string list
169 169
170 170 Allows quick awk-like usage of string lists.
171 171
172 172 Example data (in var a, created by 'a = !ls -l')::
173 173
174 174 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
175 175 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
176 176
177 177 * ``a.fields(0)`` is ``['-rwxrwxrwx', 'drwxrwxrwx+']``
178 178 * ``a.fields(1,0)`` is ``['1 -rwxrwxrwx', '6 drwxrwxrwx+']``
179 179 (note the joining by space).
180 180 * ``a.fields(-1)`` is ``['ChangeLog', 'IPython']``
181 181
182 182 IndexErrors are ignored.
183 183
184 184 Without args, fields() just split()'s the strings.
185 185 """
186 186 if len(fields) == 0:
187 187 return [el.split() for el in self]
188 188
189 189 res = SList()
190 190 for el in [f.split() for f in self]:
191 191 lineparts = []
192 192
193 193 for fd in fields:
194 194 try:
195 195 lineparts.append(el[fd])
196 196 except IndexError:
197 197 pass
198 198 if lineparts:
199 199 res.append(" ".join(lineparts))
200 200
201 201 return res
202 202
203 203 def sort(self,field= None, nums = False):
204 204 """ sort by specified fields (see fields())
205 205
206 206 Example::
207 207
208 208 a.sort(1, nums = True)
209 209
210 210 Sorts a by second field, in numerical order (so that 21 > 3)
211 211
212 212 """
213 213
214 214 #decorate, sort, undecorate
215 215 if field is not None:
216 216 dsu = [[SList([line]).fields(field), line] for line in self]
217 217 else:
218 218 dsu = [[line, line] for line in self]
219 219 if nums:
220 220 for i in range(len(dsu)):
221 221 numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()])
222 222 try:
223 223 n = int(numstr)
224 224 except ValueError:
225 225 n = 0
226 226 dsu[i][0] = n
227 227
228 228
229 229 dsu.sort()
230 230 return SList([t[1] for t in dsu])
231 231
232 232
233 233 # FIXME: We need to reimplement type specific displayhook and then add this
234 234 # back as a custom printer. This should also be moved outside utils into the
235 235 # core.
236 236
237 237 # def print_slist(arg):
238 238 # """ Prettier (non-repr-like) and more informative printer for SList """
239 239 # print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):"
240 240 # if hasattr(arg, 'hideonce') and arg.hideonce:
241 241 # arg.hideonce = False
242 242 # return
243 243 #
244 244 # nlprint(arg) # This was a nested list printer, now removed.
245 245 #
246 246 # print_slist = result_display.when_type(SList)(print_slist)
247 247
248 248
249 249 def indent(instr,nspaces=4, ntabs=0, flatten=False):
250 250 """Indent a string a given number of spaces or tabstops.
251 251
252 252 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
253 253
254 254 Parameters
255 255 ----------
256 256
257 257 instr : basestring
258 258 The string to be indented.
259 259 nspaces : int (default: 4)
260 260 The number of spaces to be indented.
261 261 ntabs : int (default: 0)
262 262 The number of tabs to be indented.
263 263 flatten : bool (default: False)
264 264 Whether to scrub existing indentation. If True, all lines will be
265 265 aligned to the same indentation. If False, existing indentation will
266 266 be strictly increased.
267 267
268 268 Returns
269 269 -------
270 270
271 271 str|unicode : string indented by ntabs and nspaces.
272 272
273 273 """
274 274 if instr is None:
275 275 return
276 276 ind = '\t'*ntabs+' '*nspaces
277 277 if flatten:
278 278 pat = re.compile(r'^\s*', re.MULTILINE)
279 279 else:
280 280 pat = re.compile(r'^', re.MULTILINE)
281 281 outstr = re.sub(pat, ind, instr)
282 282 if outstr.endswith(os.linesep+ind):
283 283 return outstr[:-len(ind)]
284 284 else:
285 285 return outstr
286 286
287 287
288 288 def list_strings(arg):
289 289 """Always return a list of strings, given a string or list of strings
290 290 as input.
291 291
292 292 Examples
293 293 --------
294 294 ::
295 295
296 296 In [7]: list_strings('A single string')
297 297 Out[7]: ['A single string']
298 298
299 299 In [8]: list_strings(['A single string in a list'])
300 300 Out[8]: ['A single string in a list']
301 301
302 302 In [9]: list_strings(['A','list','of','strings'])
303 303 Out[9]: ['A', 'list', 'of', 'strings']
304 304 """
305 305
306 306 if isinstance(arg, str):
307 307 return [arg]
308 308 else:
309 309 return arg
310 310
311 311
312 312 def marquee(txt='',width=78,mark='*'):
313 313 """Return the input string centered in a 'marquee'.
314 314
315 315 Examples
316 316 --------
317 317 ::
318 318
319 319 In [16]: marquee('A test',40)
320 320 Out[16]: '**************** A test ****************'
321 321
322 322 In [17]: marquee('A test',40,'-')
323 323 Out[17]: '---------------- A test ----------------'
324 324
325 325 In [18]: marquee('A test',40,' ')
326 326 Out[18]: ' A test '
327 327
328 328 """
329 329 if not txt:
330 330 return (mark*width)[:width]
331 331 nmark = (width-len(txt)-2)//len(mark)//2
332 332 if nmark < 0: nmark =0
333 333 marks = mark*nmark
334 334 return '%s %s %s' % (marks,txt,marks)
335 335
336 336
337 337 ini_spaces_re = re.compile(r'^(\s+)')
338 338
339 339 def num_ini_spaces(strng):
340 340 """Return the number of initial spaces in a string"""
341 341
342 342 ini_spaces = ini_spaces_re.match(strng)
343 343 if ini_spaces:
344 344 return ini_spaces.end()
345 345 else:
346 346 return 0
347 347
348 348
349 349 def format_screen(strng):
350 350 """Format a string for screen printing.
351 351
352 352 This removes some latex-type format codes."""
353 353 # Paragraph continue
354 354 par_re = re.compile(r'\\$',re.MULTILINE)
355 355 strng = par_re.sub('',strng)
356 356 return strng
357 357
358 358
359 359 def dedent(text):
360 360 """Equivalent of textwrap.dedent that ignores unindented first line.
361 361
362 362 This means it will still dedent strings like:
363 363 '''foo
364 364 is a bar
365 365 '''
366 366
367 367 For use in wrap_paragraphs.
368 368 """
369 369
370 370 if text.startswith('\n'):
371 371 # text starts with blank line, don't ignore the first line
372 372 return textwrap.dedent(text)
373 373
374 374 # split first line
375 375 splits = text.split('\n',1)
376 376 if len(splits) == 1:
377 377 # only one line
378 378 return textwrap.dedent(text)
379 379
380 380 first, rest = splits
381 381 # dedent everything but the first line
382 382 rest = textwrap.dedent(rest)
383 383 return '\n'.join([first, rest])
384 384
385 385
386 386 def wrap_paragraphs(text, ncols=80):
387 387 """Wrap multiple paragraphs to fit a specified width.
388 388
389 389 This is equivalent to textwrap.wrap, but with support for multiple
390 390 paragraphs, as separated by empty lines.
391 391
392 392 Returns
393 393 -------
394 394
395 395 list of complete paragraphs, wrapped to fill `ncols` columns.
396 396 """
397 397 paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE)
398 398 text = dedent(text).strip()
399 399 paragraphs = paragraph_re.split(text)[::2] # every other entry is space
400 400 out_ps = []
401 401 indent_re = re.compile(r'\n\s+', re.MULTILINE)
402 402 for p in paragraphs:
403 403 # presume indentation that survives dedent is meaningful formatting,
404 404 # so don't fill unless text is flush.
405 405 if indent_re.search(p) is None:
406 406 # wrap paragraph
407 407 p = textwrap.fill(p, ncols)
408 408 out_ps.append(p)
409 409 return out_ps
410 410
411 411
412 412 def long_substr(data):
413 413 """Return the longest common substring in a list of strings.
414 414
415 415 Credit: http://stackoverflow.com/questions/2892931/longest-common-substring-from-more-than-two-strings-python
416 416 """
417 417 substr = ''
418 418 if len(data) > 1 and len(data[0]) > 0:
419 419 for i in range(len(data[0])):
420 420 for j in range(len(data[0])-i+1):
421 421 if j > len(substr) and all(data[0][i:i+j] in x for x in data):
422 422 substr = data[0][i:i+j]
423 423 elif len(data) == 1:
424 424 substr = data[0]
425 425 return substr
426 426
427 427
428 428 def strip_email_quotes(text):
429 429 """Strip leading email quotation characters ('>').
430 430
431 431 Removes any combination of leading '>' interspersed with whitespace that
432 432 appears *identically* in all lines of the input text.
433 433
434 434 Parameters
435 435 ----------
436 436 text : str
437 437
438 438 Examples
439 439 --------
440 440
441 441 Simple uses::
442 442
443 443 In [2]: strip_email_quotes('> > text')
444 444 Out[2]: 'text'
445 445
446 446 In [3]: strip_email_quotes('> > text\\n> > more')
447 447 Out[3]: 'text\\nmore'
448 448
449 449 Note how only the common prefix that appears in all lines is stripped::
450 450
451 451 In [4]: strip_email_quotes('> > text\\n> > more\\n> more...')
452 452 Out[4]: '> text\\n> more\\nmore...'
453 453
454 454 So if any line has no quote marks ('>') , then none are stripped from any
455 455 of them ::
456 456
457 457 In [5]: strip_email_quotes('> > text\\n> > more\\nlast different')
458 458 Out[5]: '> > text\\n> > more\\nlast different'
459 459 """
460 460 lines = text.splitlines()
461 461 matches = set()
462 462 for line in lines:
463 463 prefix = re.match(r'^(\s*>[ >]*)', line)
464 464 if prefix:
465 465 matches.add(prefix.group(1))
466 466 else:
467 467 break
468 468 else:
469 469 prefix = long_substr(list(matches))
470 470 if prefix:
471 471 strip = len(prefix)
472 472 text = '\n'.join([ ln[strip:] for ln in lines])
473 473 return text
474 474
475 475 def strip_ansi(source):
476 476 """
477 477 Remove ansi escape codes from text.
478 478
479 479 Parameters
480 480 ----------
481 481 source : str
482 482 Source to remove the ansi from
483 483 """
484 484 return re.sub(r'\033\[(\d|;)+?m', '', source)
485 485
486 486
487 487 class EvalFormatter(Formatter):
488 488 """A String Formatter that allows evaluation of simple expressions.
489 489
490 490 Note that this version interprets a : as specifying a format string (as per
491 491 standard string formatting), so if slicing is required, you must explicitly
492 492 create a slice.
493 493
494 494 This is to be used in templating cases, such as the parallel batch
495 495 script templates, where simple arithmetic on arguments is useful.
496 496
497 497 Examples
498 498 --------
499 499 ::
500 500
501 501 In [1]: f = EvalFormatter()
502 502 In [2]: f.format('{n//4}', n=8)
503 503 Out[2]: '2'
504 504
505 505 In [3]: f.format("{greeting[slice(2,4)]}", greeting="Hello")
506 506 Out[3]: 'll'
507 507 """
508 508 def get_field(self, name, args, kwargs):
509 509 v = eval(name, kwargs)
510 510 return v, name
511 511
512 512 #XXX: As of Python 3.4, the format string parsing no longer splits on a colon
513 513 # inside [], so EvalFormatter can handle slicing. Once we only support 3.4 and
514 514 # above, it should be possible to remove FullEvalFormatter.
515 515
516 516 class FullEvalFormatter(Formatter):
517 517 """A String Formatter that allows evaluation of simple expressions.
518 518
519 519 Any time a format key is not found in the kwargs,
520 520 it will be tried as an expression in the kwargs namespace.
521 521
522 522 Note that this version allows slicing using [1:2], so you cannot specify
523 523 a format string. Use :class:`EvalFormatter` to permit format strings.
524 524
525 525 Examples
526 526 --------
527 527 ::
528 528
529 529 In [1]: f = FullEvalFormatter()
530 530 In [2]: f.format('{n//4}', n=8)
531 531 Out[2]: '2'
532 532
533 533 In [3]: f.format('{list(range(5))[2:4]}')
534 534 Out[3]: '[2, 3]'
535 535
536 536 In [4]: f.format('{3*2}')
537 537 Out[4]: '6'
538 538 """
539 539 # copied from Formatter._vformat with minor changes to allow eval
540 540 # and replace the format_spec code with slicing
541 541 def vformat(self, format_string, args, kwargs):
542 542 result = []
543 543 for literal_text, field_name, format_spec, conversion in \
544 544 self.parse(format_string):
545 545
546 546 # output the literal text
547 547 if literal_text:
548 548 result.append(literal_text)
549 549
550 550 # if there's a field, output it
551 551 if field_name is not None:
552 552 # this is some markup, find the object and do
553 553 # the formatting
554 554
555 555 if format_spec:
556 556 # override format spec, to allow slicing:
557 557 field_name = ':'.join([field_name, format_spec])
558 558
559 559 # eval the contents of the field for the object
560 560 # to be formatted
561 561 obj = eval(field_name, kwargs)
562 562
563 563 # do any conversion on the resulting object
564 564 obj = self.convert_field(obj, conversion)
565 565
566 566 # format the object and append to the result
567 567 result.append(self.format_field(obj, ''))
568 568
569 569 return ''.join(py3compat.cast_unicode(s) for s in result)
570 570
571 571
572 572 class DollarFormatter(FullEvalFormatter):
573 573 """Formatter allowing Itpl style $foo replacement, for names and attribute
574 574 access only. Standard {foo} replacement also works, and allows full
575 575 evaluation of its arguments.
576 576
577 577 Examples
578 578 --------
579 579 ::
580 580
581 581 In [1]: f = DollarFormatter()
582 582 In [2]: f.format('{n//4}', n=8)
583 583 Out[2]: '2'
584 584
585 585 In [3]: f.format('23 * 76 is $result', result=23*76)
586 586 Out[3]: '23 * 76 is 1748'
587 587
588 588 In [4]: f.format('$a or {b}', a=1, b=2)
589 589 Out[4]: '1 or 2'
590 590 """
591 _dollar_pattern_ignore_single_quote = re.compile("(.*?)\$(\$?[\w\.]+)(?=([^']*'[^']*')*[^']*$)")
591 _dollar_pattern_ignore_single_quote = re.compile(r"(.*?)\$(\$?[\w\.]+)(?=([^']*'[^']*')*[^']*$)")
592 592 def parse(self, fmt_string):
593 593 for literal_txt, field_name, format_spec, conversion \
594 594 in Formatter.parse(self, fmt_string):
595 595
596 596 # Find $foo patterns in the literal text.
597 597 continue_from = 0
598 598 txt = ""
599 599 for m in self._dollar_pattern_ignore_single_quote.finditer(literal_txt):
600 600 new_txt, new_field = m.group(1,2)
601 601 # $$foo --> $foo
602 602 if new_field.startswith("$"):
603 603 txt += new_txt + new_field
604 604 else:
605 605 yield (txt + new_txt, new_field, "", None)
606 606 txt = ""
607 607 continue_from = m.end()
608 608
609 609 # Re-yield the {foo} style pattern
610 610 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
611 611
612 612 #-----------------------------------------------------------------------------
613 613 # Utils to columnize a list of string
614 614 #-----------------------------------------------------------------------------
615 615
616 616 def _col_chunks(l, max_rows, row_first=False):
617 617 """Yield successive max_rows-sized column chunks from l."""
618 618 if row_first:
619 619 ncols = (len(l) // max_rows) + (len(l) % max_rows > 0)
620 620 for i in range(ncols):
621 621 yield [l[j] for j in range(i, len(l), ncols)]
622 622 else:
623 623 for i in range(0, len(l), max_rows):
624 624 yield l[i:(i + max_rows)]
625 625
626 626
627 627 def _find_optimal(rlist, row_first=False, separator_size=2, displaywidth=80):
628 628 """Calculate optimal info to columnize a list of string"""
629 629 for max_rows in range(1, len(rlist) + 1):
630 630 col_widths = list(map(max, _col_chunks(rlist, max_rows, row_first)))
631 631 sumlength = sum(col_widths)
632 632 ncols = len(col_widths)
633 633 if sumlength + separator_size * (ncols - 1) <= displaywidth:
634 634 break
635 635 return {'num_columns': ncols,
636 636 'optimal_separator_width': (displaywidth - sumlength) // (ncols - 1) if (ncols - 1) else 0,
637 637 'max_rows': max_rows,
638 638 'column_widths': col_widths
639 639 }
640 640
641 641
642 642 def _get_or_default(mylist, i, default=None):
643 643 """return list item number, or default if don't exist"""
644 644 if i >= len(mylist):
645 645 return default
646 646 else :
647 647 return mylist[i]
648 648
649 649
650 650 def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) :
651 651 """Returns a nested list, and info to columnize items
652 652
653 653 Parameters
654 654 ----------
655 655
656 656 items
657 657 list of strings to columize
658 658 row_first : (default False)
659 659 Whether to compute columns for a row-first matrix instead of
660 660 column-first (default).
661 661 empty : (default None)
662 662 default value to fill list if needed
663 663 separator_size : int (default=2)
664 664 How much characters will be used as a separation between each columns.
665 665 displaywidth : int (default=80)
666 666 The width of the area onto which the columns should enter
667 667
668 668 Returns
669 669 -------
670 670
671 671 strings_matrix
672 672
673 673 nested list of string, the outer most list contains as many list as
674 674 rows, the innermost lists have each as many element as columns. If the
675 675 total number of elements in `items` does not equal the product of
676 676 rows*columns, the last element of some lists are filled with `None`.
677 677
678 678 dict_info
679 679 some info to make columnize easier:
680 680
681 681 num_columns
682 682 number of columns
683 683 max_rows
684 684 maximum number of rows (final number may be less)
685 685 column_widths
686 686 list of with of each columns
687 687 optimal_separator_width
688 688 best separator width between columns
689 689
690 690 Examples
691 691 --------
692 692 ::
693 693
694 694 In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l']
695 695 In [2]: list, info = compute_item_matrix(l, displaywidth=12)
696 696 In [3]: list
697 697 Out[3]: [['aaa', 'f', 'k'], ['b', 'g', 'l'], ['cc', 'h', None], ['d', 'i', None], ['eeeee', 'j', None]]
698 698 In [4]: ideal = {'num_columns': 3, 'column_widths': [5, 1, 1], 'optimal_separator_width': 2, 'max_rows': 5}
699 699 In [5]: all((info[k] == ideal[k] for k in ideal.keys()))
700 700 Out[5]: True
701 701 """
702 702 info = _find_optimal(list(map(len, items)), row_first, *args, **kwargs)
703 703 nrow, ncol = info['max_rows'], info['num_columns']
704 704 if row_first:
705 705 return ([[_get_or_default(items, r * ncol + c, default=empty) for c in range(ncol)] for r in range(nrow)], info)
706 706 else:
707 707 return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info)
708 708
709 709
710 710 def columnize(items, row_first=False, separator=' ', displaywidth=80, spread=False):
711 711 """ Transform a list of strings into a single string with columns.
712 712
713 713 Parameters
714 714 ----------
715 715 items : sequence of strings
716 716 The strings to process.
717 717
718 718 row_first : (default False)
719 719 Whether to compute columns for a row-first matrix instead of
720 720 column-first (default).
721 721
722 722 separator : str, optional [default is two spaces]
723 723 The string that separates columns.
724 724
725 725 displaywidth : int, optional [default is 80]
726 726 Width of the display in number of characters.
727 727
728 728 Returns
729 729 -------
730 730 The formatted string.
731 731 """
732 732 if not items:
733 733 return '\n'
734 734 matrix, info = compute_item_matrix(items, row_first=row_first, separator_size=len(separator), displaywidth=displaywidth)
735 735 if spread:
736 736 separator = separator.ljust(int(info['optimal_separator_width']))
737 737 fmatrix = [filter(None, x) for x in matrix]
738 738 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['column_widths'])])
739 739 return '\n'.join(map(sjoin, fmatrix))+'\n'
740 740
741 741
742 742 def get_text_list(list_, last_sep=' and ', sep=", ", wrap_item_with=""):
743 743 """
744 744 Return a string with a natural enumeration of items
745 745
746 746 >>> get_text_list(['a', 'b', 'c', 'd'])
747 747 'a, b, c and d'
748 748 >>> get_text_list(['a', 'b', 'c'], ' or ')
749 749 'a, b or c'
750 750 >>> get_text_list(['a', 'b', 'c'], ', ')
751 751 'a, b, c'
752 752 >>> get_text_list(['a', 'b'], ' or ')
753 753 'a or b'
754 754 >>> get_text_list(['a'])
755 755 'a'
756 756 >>> get_text_list([])
757 757 ''
758 758 >>> get_text_list(['a', 'b'], wrap_item_with="`")
759 759 '`a` and `b`'
760 760 >>> get_text_list(['a', 'b', 'c', 'd'], " = ", sep=" + ")
761 761 'a + b + c = d'
762 762 """
763 763 if len(list_) == 0:
764 764 return ''
765 765 if wrap_item_with:
766 766 list_ = ['%s%s%s' % (wrap_item_with, item, wrap_item_with) for
767 767 item in list_]
768 768 if len(list_) == 1:
769 769 return list_[0]
770 770 return '%s%s%s' % (
771 771 sep.join(i for i in list_[:-1]),
772 772 last_sep, list_[-1])
General Comments 0
You need to be logged in to leave comments. Login now