##// END OF EJS Templates
IPython/Extensions/ipipe.py: Added a Table ihist that can be used to...
walter.doerwald -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,1727 +1,1765 b''
1 1 # -*- coding: iso-8859-1 -*-
2 2
3 3 import curses, fcntl, signal, struct, tty, textwrap, inspect
4 4
5 from IPython import ipapi
6
5 7 import astyle, ipipe
6 8
7 9
8 10 # Python 2.3 compatibility
9 11 try:
10 12 set
11 13 except NameError:
12 14 import sets
13 15 set = sets.Set
14 16
15 17 # Python 2.3 compatibility
16 18 try:
17 19 sorted
18 20 except NameError:
19 21 from ipipe import sorted
20 22
21 23
22 24 class UnassignedKeyError(Exception):
23 25 """
24 26 Exception that is used for reporting unassigned keys.
25 27 """
26 28
27 29
28 30 class UnknownCommandError(Exception):
29 31 """
30 32 Exception that is used for reporting unknown commands (this should never
31 33 happen).
32 34 """
33 35
34 36
35 37 class CommandError(Exception):
36 38 """
37 39 Exception that is used for reporting that a command can't be executed.
38 40 """
39 41
40 42
41 43 class Keymap(dict):
42 44 """
43 45 Stores mapping of keys to commands.
44 46 """
45 47 def __init__(self):
46 48 self._keymap = {}
47 49
48 50 def __setitem__(self, key, command):
49 51 if isinstance(key, str):
50 52 for c in key:
51 53 dict.__setitem__(self, ord(c), command)
52 54 else:
53 55 dict.__setitem__(self, key, command)
54 56
55 57 def __getitem__(self, key):
56 58 if isinstance(key, str):
57 59 key = ord(key)
58 60 return dict.__getitem__(self, key)
59 61
60 62 def __detitem__(self, key):
61 63 if isinstance(key, str):
62 64 key = ord(key)
63 65 dict.__detitem__(self, key)
64 66
65 67 def register(self, command, *keys):
66 68 for key in keys:
67 69 self[key] = command
68 70
69 71 def get(self, key, default=None):
70 72 if isinstance(key, str):
71 73 key = ord(key)
72 74 return dict.get(self, key, default)
73 75
74 76 def findkey(self, command, default=ipipe.noitem):
75 77 for (key, commandcandidate) in self.iteritems():
76 78 if commandcandidate == command:
77 79 return key
78 80 if default is ipipe.noitem:
79 81 raise KeyError(command)
80 82 return default
81 83
82 84
83 85 class _BrowserCachedItem(object):
84 86 # This is used internally by ``ibrowse`` to store a item together with its
85 87 # marked status.
86 88 __slots__ = ("item", "marked")
87 89
88 90 def __init__(self, item):
89 91 self.item = item
90 92 self.marked = False
91 93
92 94
93 95 class _BrowserHelp(object):
94 96 style_header = astyle.Style.fromstr("yellow:black:bold")
95 97 # This is used internally by ``ibrowse`` for displaying the help screen.
96 98 def __init__(self, browser):
97 99 self.browser = browser
98 100
99 101 def __xrepr__(self, mode):
100 102 yield (-1, True)
101 103 if mode == "header" or mode == "footer":
102 104 yield (astyle.style_default, "ibrowse help screen")
103 105 else:
104 106 yield (astyle.style_default, repr(self))
105 107
106 108 def __iter__(self):
107 109 # Get reverse key mapping
108 110 allkeys = {}
109 111 for (key, cmd) in self.browser.keymap.iteritems():
110 112 allkeys.setdefault(cmd, []).append(key)
111 113
112 114 fields = ("key", "description")
113 115
114 116 commands = []
115 117 for name in dir(self.browser):
116 118 if name.startswith("cmd_"):
117 119 command = getattr(self.browser, name)
118 120 commands.append((inspect.getsourcelines(command)[-1], name[4:], command))
119 121 commands.sort()
120 122 commands = [(c[1], c[2]) for c in commands]
121 123 for (i, (name, command)) in enumerate(commands):
122 124 if i:
123 125 yield ipipe.Fields(fields, key="", description="")
124 126
125 127 description = command.__doc__
126 128 if description is None:
127 129 lines = []
128 130 else:
129 131 lines = [l.strip() for l in description.splitlines() if l.strip()]
130 132 description = "\n".join(lines)
131 133 lines = textwrap.wrap(description, 60)
132 134 keys = allkeys.get(name, [])
133 135
134 136 yield ipipe.Fields(fields, key="", description=astyle.Text((self.style_header, name)))
135 137 for i in xrange(max(len(keys), len(lines))):
136 138 try:
137 139 key = self.browser.keylabel(keys[i])
138 140 except IndexError:
139 141 key = ""
140 142 try:
141 143 line = lines[i]
142 144 except IndexError:
143 145 line = ""
144 146 yield ipipe.Fields(fields, key=key, description=line)
145 147
146 148
147 149 class _BrowserLevel(object):
148 150 # This is used internally to store the state (iterator, fetch items,
149 151 # position of cursor and screen, etc.) of one browser level
150 152 # An ``ibrowse`` object keeps multiple ``_BrowserLevel`` objects in
151 153 # a stack.
152 154 def __init__(self, browser, input, mainsizey, *attrs):
153 155 self.browser = browser
154 156 self.input = input
155 157 self.header = [x for x in ipipe.xrepr(input, "header") if not isinstance(x[0], int)]
156 158 # iterator for the input
157 159 self.iterator = ipipe.xiter(input)
158 160
159 161 # is the iterator exhausted?
160 162 self.exhausted = False
161 163
162 164 # attributes to be display (autodetected if empty)
163 165 self.attrs = attrs
164 166
165 167 # fetched items (+ marked flag)
166 168 self.items = ipipe.deque()
167 169
168 170 # Number of marked objects
169 171 self.marked = 0
170 172
171 173 # Vertical cursor position
172 174 self.cury = 0
173 175
174 176 # Horizontal cursor position
175 177 self.curx = 0
176 178
177 179 # Index of first data column
178 180 self.datastartx = 0
179 181
180 182 # Index of first data line
181 183 self.datastarty = 0
182 184
183 185 # height of the data display area
184 186 self.mainsizey = mainsizey
185 187
186 188 # width of the data display area (changes when scrolling)
187 189 self.mainsizex = 0
188 190
189 191 # Size of row number (changes when scrolling)
190 192 self.numbersizex = 0
191 193
192 194 # Attributes to display (in this order)
193 195 self.displayattrs = []
194 196
195 197 # index and attribute under the cursor
196 198 self.displayattr = (None, ipipe.noitem)
197 199
198 200 # Maps attributes to column widths
199 201 self.colwidths = {}
200 202
201 203 # Set of hidden attributes
202 204 self.hiddenattrs = set()
203 205
204 206 # This takes care of all the caches etc.
205 207 self.moveto(0, 0, refresh=True)
206 208
207 209 def fetch(self, count):
208 210 # Try to fill ``self.items`` with at least ``count`` objects.
209 211 have = len(self.items)
210 212 while not self.exhausted and have < count:
211 213 try:
212 214 item = self.iterator.next()
213 215 except StopIteration:
214 216 self.exhausted = True
215 217 break
216 218 except (KeyboardInterrupt, SystemExit):
217 219 raise
218 220 except Exception, exc:
219 221 have += 1
220 222 self.items.append(_BrowserCachedItem(exc))
221 223 self.exhausted = True
222 224 break
223 225 else:
224 226 have += 1
225 227 self.items.append(_BrowserCachedItem(item))
226 228
227 229 def calcdisplayattrs(self):
228 230 # Calculate which attributes are available from the objects that are
229 231 # currently visible on screen (and store it in ``self.displayattrs``)
230 232
231 233 attrs = set()
232 234 self.displayattrs = []
233 235 if self.attrs:
234 236 # If the browser object specifies a fixed list of attributes,
235 237 # simply use it (removing hidden attributes).
236 238 for attr in self.attrs:
237 239 attr = ipipe.upgradexattr(attr)
238 240 if attr not in attrs and attr not in self.hiddenattrs:
239 241 self.displayattrs.append(attr)
240 242 attrs.add(attr)
241 243 else:
242 244 endy = min(self.datastarty+self.mainsizey, len(self.items))
243 245 for i in xrange(self.datastarty, endy):
244 246 for attr in ipipe.xattrs(self.items[i].item, "default"):
245 247 if attr not in attrs and attr not in self.hiddenattrs:
246 248 self.displayattrs.append(attr)
247 249 attrs.add(attr)
248 250
249 251 def getrow(self, i):
250 252 # Return a dictionary with the attributes for the object
251 253 # ``self.items[i]``. Attribute names are taken from
252 254 # ``self.displayattrs`` so ``calcdisplayattrs()`` must have been
253 255 # called before.
254 256 row = {}
255 257 item = self.items[i].item
256 258 for attr in self.displayattrs:
257 259 try:
258 260 value = attr.value(item)
259 261 except (KeyboardInterrupt, SystemExit):
260 262 raise
261 263 except Exception, exc:
262 264 value = exc
263 265 # only store attribute if it exists (or we got an exception)
264 266 if value is not ipipe.noitem:
265 267 # remember alignment, length and colored text
266 268 row[attr] = ipipe.xformat(value, "cell", self.browser.maxattrlength)
267 269 return row
268 270
269 271 def calcwidths(self):
270 272 # Recalculate the displayed fields and their widths.
271 273 # ``calcdisplayattrs()'' must have been called and the cache
272 274 # for attributes of the objects on screen (``self.displayrows``)
273 275 # must have been filled. This sets ``self.colwidths`` which maps
274 276 # attribute descriptors to widths.
275 277 self.colwidths = {}
276 278 for row in self.displayrows:
277 279 for attr in self.displayattrs:
278 280 try:
279 281 length = row[attr][1]
280 282 except KeyError:
281 283 length = 0
282 284 # always add attribute to colwidths, even if it doesn't exist
283 285 if attr not in self.colwidths:
284 286 self.colwidths[attr] = len(attr.name())
285 287 newwidth = max(self.colwidths[attr], length)
286 288 self.colwidths[attr] = newwidth
287 289
288 290 # How many characters do we need to paint the largest item number?
289 291 self.numbersizex = len(str(self.datastarty+self.mainsizey-1))
290 292 # How must space have we got to display data?
291 293 self.mainsizex = self.browser.scrsizex-self.numbersizex-3
292 294 # width of all columns
293 295 self.datasizex = sum(self.colwidths.itervalues()) + len(self.colwidths)
294 296
295 297 def calcdisplayattr(self):
296 298 # Find out which attribute the cursor is on and store this
297 299 # information in ``self.displayattr``.
298 300 pos = 0
299 301 for (i, attr) in enumerate(self.displayattrs):
300 302 if pos+self.colwidths[attr] >= self.curx:
301 303 self.displayattr = (i, attr)
302 304 break
303 305 pos += self.colwidths[attr]+1
304 306 else:
305 307 self.displayattr = (None, ipipe.noitem)
306 308
307 309 def moveto(self, x, y, refresh=False):
308 310 # Move the cursor to the position ``(x,y)`` (in data coordinates,
309 311 # not in screen coordinates). If ``refresh`` is true, all cached
310 312 # values will be recalculated (e.g. because the list has been
311 313 # resorted, so screen positions etc. are no longer valid).
312 314 olddatastarty = self.datastarty
313 315 oldx = self.curx
314 316 oldy = self.cury
315 317 x = int(x+0.5)
316 318 y = int(y+0.5)
317 319 newx = x # remember where we wanted to move
318 320 newy = y # remember where we wanted to move
319 321
320 322 scrollbordery = min(self.browser.scrollbordery, self.mainsizey//2)
321 323 scrollborderx = min(self.browser.scrollborderx, self.mainsizex//2)
322 324
323 325 # Make sure that the cursor didn't leave the main area vertically
324 326 if y < 0:
325 327 y = 0
326 328 # try to get enough items to fill the screen
327 329 self.fetch(max(y+scrollbordery+1, self.mainsizey))
328 330 if y >= len(self.items):
329 331 y = max(0, len(self.items)-1)
330 332
331 333 # Make sure that the cursor stays on screen vertically
332 334 if y < self.datastarty+scrollbordery:
333 335 self.datastarty = max(0, y-scrollbordery)
334 336 elif y >= self.datastarty+self.mainsizey-scrollbordery:
335 337 self.datastarty = max(0, min(y-self.mainsizey+scrollbordery+1,
336 338 len(self.items)-self.mainsizey))
337 339
338 340 if refresh: # Do we need to refresh the complete display?
339 341 self.calcdisplayattrs()
340 342 endy = min(self.datastarty+self.mainsizey, len(self.items))
341 343 self.displayrows = map(self.getrow, xrange(self.datastarty, endy))
342 344 self.calcwidths()
343 345 # Did we scroll vertically => update displayrows
344 346 # and various other attributes
345 347 elif self.datastarty != olddatastarty:
346 348 # Recalculate which attributes we have to display
347 349 olddisplayattrs = self.displayattrs
348 350 self.calcdisplayattrs()
349 351 # If there are new attributes, recreate the cache
350 352 if self.displayattrs != olddisplayattrs:
351 353 endy = min(self.datastarty+self.mainsizey, len(self.items))
352 354 self.displayrows = map(self.getrow, xrange(self.datastarty, endy))
353 355 elif self.datastarty<olddatastarty: # we did scroll up
354 356 # drop rows from the end
355 357 del self.displayrows[self.datastarty-olddatastarty:]
356 358 # fetch new items
357 359 for i in xrange(min(olddatastarty, self.datastarty+self.mainsizey)-1,
358 360 self.datastarty-1, -1):
359 361 try:
360 362 row = self.getrow(i)
361 363 except IndexError:
362 364 # we didn't have enough objects to fill the screen
363 365 break
364 366 self.displayrows.insert(0, row)
365 367 else: # we did scroll down
366 368 # drop rows from the start
367 369 del self.displayrows[:self.datastarty-olddatastarty]
368 370 # fetch new items
369 371 for i in xrange(max(olddatastarty+self.mainsizey, self.datastarty),
370 372 self.datastarty+self.mainsizey):
371 373 try:
372 374 row = self.getrow(i)
373 375 except IndexError:
374 376 # we didn't have enough objects to fill the screen
375 377 break
376 378 self.displayrows.append(row)
377 379 self.calcwidths()
378 380
379 381 # Make sure that the cursor didn't leave the data area horizontally
380 382 if x < 0:
381 383 x = 0
382 384 elif x >= self.datasizex:
383 385 x = max(0, self.datasizex-1)
384 386
385 387 # Make sure that the cursor stays on screen horizontally
386 388 if x < self.datastartx+scrollborderx:
387 389 self.datastartx = max(0, x-scrollborderx)
388 390 elif x >= self.datastartx+self.mainsizex-scrollborderx:
389 391 self.datastartx = max(0, min(x-self.mainsizex+scrollborderx+1,
390 392 self.datasizex-self.mainsizex))
391 393
392 394 if x == oldx and y == oldy and (x != newx or y != newy): # couldn't move
393 395 self.browser.beep()
394 396 else:
395 397 self.curx = x
396 398 self.cury = y
397 399 self.calcdisplayattr()
398 400
399 401 def sort(self, key, reverse=False):
400 402 """
401 403 Sort the currently list of items using the key function ``key``. If
402 404 ``reverse`` is true the sort order is reversed.
403 405 """
404 406 curitem = self.items[self.cury] # Remember where the cursor is now
405 407
406 408 # Sort items
407 409 def realkey(item):
408 410 return key(item.item)
409 411 self.items = ipipe.deque(sorted(self.items, key=realkey, reverse=reverse))
410 412
411 413 # Find out where the object under the cursor went
412 414 cury = self.cury
413 415 for (i, item) in enumerate(self.items):
414 416 if item is curitem:
415 417 cury = i
416 418 break
417 419
418 420 self.moveto(self.curx, cury, refresh=True)
419 421
420 422 def refresh(self):
421 423 """
422 424 Restart iterating the input.
423 425 """
424 426 self.iterator = ipipe.xiter(self.input)
425 427 self.items.clear()
426 428 self.exhausted = False
427 429 self.datastartx = self.datastarty = 0
428 430 self.moveto(0, 0, refresh=True)
429 431
430 432 def refreshfind(self):
431 433 """
432 434 Restart iterating the input and go back to the same object as before
433 435 (if it can be found in the new iterator).
434 436 """
435 437 try:
436 438 oldobject = self.items[self.cury].item
437 439 except IndexError:
438 440 oldobject = ipipe.noitem
439 441 self.iterator = ipipe.xiter(self.input)
440 442 self.items.clear()
441 443 self.exhausted = False
442 444 while True:
443 445 self.fetch(len(self.items)+1)
444 446 if self.exhausted:
445 447 curses.beep()
446 448 self.datastartx = self.datastarty = 0
447 449 self.moveto(self.curx, 0, refresh=True)
448 450 break
449 451 if self.items[-1].item == oldobject:
450 452 self.datastartx = self.datastarty = 0
451 453 self.moveto(self.curx, len(self.items)-1, refresh=True)
452 454 break
453 455
454 456
455 457 class _CommandInput(object):
456 458 keymap = Keymap()
457 459 keymap.register("left", curses.KEY_LEFT)
458 460 keymap.register("right", curses.KEY_RIGHT)
459 461 keymap.register("home", curses.KEY_HOME, "\x01") # Ctrl-A
460 462 keymap.register("end", curses.KEY_END, "\x05") # Ctrl-E
461 463 # FIXME: What's happening here?
462 464 keymap.register("backspace", curses.KEY_BACKSPACE, "\x08\x7f")
463 465 keymap.register("delete", curses.KEY_DC)
464 466 keymap.register("delend", 0x0b) # Ctrl-K
465 467 keymap.register("execute", "\r\n")
466 468 keymap.register("up", curses.KEY_UP)
467 469 keymap.register("down", curses.KEY_DOWN)
468 470 keymap.register("incsearchup", curses.KEY_PPAGE)
469 471 keymap.register("incsearchdown", curses.KEY_NPAGE)
470 472 keymap.register("exit", "\x18"), # Ctrl-X
471 473
472 474 def __init__(self, prompt):
473 475 self.prompt = prompt
474 476 self.history = []
475 477 self.maxhistory = 100
476 478 self.input = ""
477 479 self.curx = 0
478 480 self.cury = -1 # blank line
479 481
480 482 def start(self):
481 483 self.input = ""
482 484 self.curx = 0
483 485 self.cury = -1 # blank line
484 486
485 487 def handlekey(self, browser, key):
486 488 cmdname = self.keymap.get(key, None)
487 489 if cmdname is not None:
488 490 cmdfunc = getattr(self, "cmd_%s" % cmdname, None)
489 491 if cmdfunc is not None:
490 492 return cmdfunc(browser)
491 493 curses.beep()
492 494 elif key != -1:
493 495 try:
494 496 char = chr(key)
495 497 except ValueError:
496 498 curses.beep()
497 499 else:
498 500 return self.handlechar(browser, char)
499 501
500 502 def handlechar(self, browser, char):
501 503 self.input = self.input[:self.curx] + char + self.input[self.curx:]
502 504 self.curx += 1
503 505 return True
504 506
505 507 def dohistory(self):
506 508 self.history.insert(0, self.input)
507 509 del self.history[:-self.maxhistory]
508 510
509 511 def cmd_backspace(self, browser):
510 512 if self.curx:
511 513 self.input = self.input[:self.curx-1] + self.input[self.curx:]
512 514 self.curx -= 1
513 515 return True
514 516 else:
515 517 curses.beep()
516 518
517 519 def cmd_delete(self, browser):
518 520 if self.curx<len(self.input):
519 521 self.input = self.input[:self.curx] + self.input[self.curx+1:]
520 522 return True
521 523 else:
522 524 curses.beep()
523 525
524 526 def cmd_delend(self, browser):
525 527 if self.curx<len(self.input):
526 528 self.input = self.input[:self.curx]
527 529 return True
528 530
529 531 def cmd_left(self, browser):
530 532 if self.curx:
531 533 self.curx -= 1
532 534 return True
533 535 else:
534 536 curses.beep()
535 537
536 538 def cmd_right(self, browser):
537 539 if self.curx < len(self.input):
538 540 self.curx += 1
539 541 return True
540 542 else:
541 543 curses.beep()
542 544
543 545 def cmd_home(self, browser):
544 546 if self.curx:
545 547 self.curx = 0
546 548 return True
547 549 else:
548 550 curses.beep()
549 551
550 552 def cmd_end(self, browser):
551 553 if self.curx < len(self.input):
552 554 self.curx = len(self.input)
553 555 return True
554 556 else:
555 557 curses.beep()
556 558
557 559 def cmd_up(self, browser):
558 560 if self.cury < len(self.history)-1:
559 561 self.cury += 1
560 562 self.input = self.history[self.cury]
561 563 self.curx = len(self.input)
562 564 return True
563 565 else:
564 566 curses.beep()
565 567
566 568 def cmd_down(self, browser):
567 569 if self.cury >= 0:
568 570 self.cury -= 1
569 571 if self.cury>=0:
570 572 self.input = self.history[self.cury]
571 573 else:
572 574 self.input = ""
573 575 self.curx = len(self.input)
574 576 return True
575 577 else:
576 578 curses.beep()
577 579
578 580 def cmd_incsearchup(self, browser):
579 581 prefix = self.input[:self.curx]
580 582 cury = self.cury
581 583 while True:
582 584 cury += 1
583 585 if cury >= len(self.history):
584 586 break
585 587 if self.history[cury].startswith(prefix):
586 588 self.input = self.history[cury]
587 589 self.cury = cury
588 590 return True
589 591 curses.beep()
590 592
591 593 def cmd_incsearchdown(self, browser):
592 594 prefix = self.input[:self.curx]
593 595 cury = self.cury
594 596 while True:
595 597 cury -= 1
596 598 if cury <= 0:
597 599 break
598 600 if self.history[cury].startswith(prefix):
599 601 self.input = self.history[self.cury]
600 602 self.cury = cury
601 603 return True
602 604 curses.beep()
603 605
604 606 def cmd_exit(self, browser):
605 607 browser.mode = "default"
606 608 return True
607 609
608 610 def cmd_execute(self, browser):
609 611 raise NotImplementedError
610 612
611 613
612 614 class _CommandGoto(_CommandInput):
613 615 def __init__(self):
614 616 _CommandInput.__init__(self, "goto object #")
615 617
616 618 def handlechar(self, browser, char):
617 619 # Only accept digits
618 620 if not "0" <= char <= "9":
619 621 curses.beep()
620 622 else:
621 623 return _CommandInput.handlechar(self, browser, char)
622 624
623 625 def cmd_execute(self, browser):
624 626 level = browser.levels[-1]
625 627 if self.input:
626 628 self.dohistory()
627 629 level.moveto(level.curx, int(self.input))
628 630 browser.mode = "default"
629 631 return True
630 632
631 633
632 634 class _CommandFind(_CommandInput):
633 635 def __init__(self):
634 636 _CommandInput.__init__(self, "find expression")
635 637
636 638 def cmd_execute(self, browser):
637 639 level = browser.levels[-1]
638 640 if self.input:
639 641 self.dohistory()
640 642 while True:
641 643 cury = level.cury
642 644 level.moveto(level.curx, cury+1)
643 645 if cury == level.cury:
644 646 curses.beep()
645 647 break # hit end
646 648 item = level.items[level.cury].item
647 649 try:
648 650 globals = ipipe.getglobals(None)
649 651 if eval(self.input, globals, ipipe.AttrNamespace(item)):
650 652 break # found something
651 653 except (KeyboardInterrupt, SystemExit):
652 654 raise
653 655 except Exception, exc:
654 656 browser.report(exc)
655 657 curses.beep()
656 658 break # break on error
657 659 browser.mode = "default"
658 660 return True
659 661
660 662
661 663 class _CommandFindBackwards(_CommandInput):
662 664 def __init__(self):
663 665 _CommandInput.__init__(self, "find backwards expression")
664 666
665 667 def cmd_execute(self, browser):
666 668 level = browser.levels[-1]
667 669 if self.input:
668 670 self.dohistory()
669 671 while level.cury:
670 672 level.moveto(level.curx, level.cury-1)
671 673 item = level.items[level.cury].item
672 674 try:
673 675 globals = ipipe.getglobals(None)
674 676 if eval(self.input, globals, ipipe.AttrNamespace(item)):
675 677 break # found something
676 678 except (KeyboardInterrupt, SystemExit):
677 679 raise
678 680 except Exception, exc:
679 681 browser.report(exc)
680 682 curses.beep()
681 683 break # break on error
682 684 else:
683 685 curses.beep()
684 686 browser.mode = "default"
685 687 return True
686 688
687 689
688 690 class ibrowse(ipipe.Display):
689 691 # Show this many lines from the previous screen when paging horizontally
690 692 pageoverlapx = 1
691 693
692 694 # Show this many lines from the previous screen when paging vertically
693 695 pageoverlapy = 1
694 696
695 697 # Start scrolling when the cursor is less than this number of columns
696 698 # away from the left or right screen edge
697 699 scrollborderx = 10
698 700
699 701 # Start scrolling when the cursor is less than this number of lines
700 702 # away from the top or bottom screen edge
701 703 scrollbordery = 5
702 704
703 705 # Accelerate by this factor when scrolling horizontally
704 706 acceleratex = 1.05
705 707
706 708 # Accelerate by this factor when scrolling vertically
707 709 acceleratey = 1.05
708 710
709 711 # The maximum horizontal scroll speed
710 712 # (as a factor of the screen width (i.e. 0.5 == half a screen width)
711 713 maxspeedx = 0.5
712 714
713 715 # The maximum vertical scroll speed
714 716 # (as a factor of the screen height (i.e. 0.5 == half a screen height)
715 717 maxspeedy = 0.5
716 718
717 719 # The maximum number of header lines for browser level
718 720 # if the nesting is deeper, only the innermost levels are displayed
719 721 maxheaders = 5
720 722
721 723 # The approximate maximum length of a column entry
722 724 maxattrlength = 200
723 725
724 726 # Styles for various parts of the GUI
725 727 style_objheadertext = astyle.Style.fromstr("white:black:bold|reverse")
726 728 style_objheadernumber = astyle.Style.fromstr("white:blue:bold|reverse")
727 729 style_objheaderobject = astyle.Style.fromstr("white:black:reverse")
728 730 style_colheader = astyle.Style.fromstr("blue:white:reverse")
729 731 style_colheaderhere = astyle.Style.fromstr("green:black:bold|reverse")
730 732 style_colheadersep = astyle.Style.fromstr("blue:black:reverse")
731 733 style_number = astyle.Style.fromstr("blue:white:reverse")
732 734 style_numberhere = astyle.Style.fromstr("green:black:bold|reverse")
733 735 style_sep = astyle.Style.fromstr("blue:black")
734 736 style_data = astyle.Style.fromstr("white:black")
735 737 style_datapad = astyle.Style.fromstr("blue:black:bold")
736 738 style_footer = astyle.Style.fromstr("black:white")
737 739 style_report = astyle.Style.fromstr("white:black")
738 740
739 741 # Column separator in header
740 742 headersepchar = "|"
741 743
742 744 # Character for padding data cell entries
743 745 datapadchar = "."
744 746
745 747 # Column separator in data area
746 748 datasepchar = "|"
747 749
748 750 # Character to use for "empty" cell (i.e. for non-existing attributes)
749 751 nodatachar = "-"
750 752
751 753 # Prompts for modes that require keyboard input
752 754 prompts = {
753 755 "goto": _CommandGoto(),
754 756 "find": _CommandFind(),
755 757 "findbackwards": _CommandFindBackwards()
756 758 }
757 759
758 760 # Maps curses key codes to "function" names
759 761 keymap = Keymap()
760 762 keymap.register("quit", "q")
761 763 keymap.register("up", curses.KEY_UP)
762 764 keymap.register("down", curses.KEY_DOWN)
763 765 keymap.register("pageup", curses.KEY_PPAGE)
764 766 keymap.register("pagedown", curses.KEY_NPAGE)
765 767 keymap.register("left", curses.KEY_LEFT)
766 768 keymap.register("right", curses.KEY_RIGHT)
767 769 keymap.register("home", curses.KEY_HOME, "\x01")
768 770 keymap.register("end", curses.KEY_END, "\x05")
769 771 keymap.register("prevattr", "<\x1b")
770 772 keymap.register("nextattr", ">\t")
771 773 keymap.register("pick", "p")
772 774 keymap.register("pickattr", "P")
773 775 keymap.register("pickallattrs", "C")
774 776 keymap.register("pickmarked", "m")
775 777 keymap.register("pickmarkedattr", "M")
778 keymap.register("pickinput", "i")
779 keymap.register("pickinputattr", "I")
776 780 keymap.register("hideattr", "h")
777 781 keymap.register("unhideattrs", "H")
778 782 keymap.register("help", "?")
779 783 keymap.register("enter", "\r\n")
780 784 keymap.register("enterattr", "E")
781 785 # FIXME: What's happening here?
782 786 keymap.register("leave", curses.KEY_BACKSPACE, "x\x08\x7f")
783 787 keymap.register("detail", "d")
784 788 keymap.register("detailattr", "D")
785 789 keymap.register("tooglemark", " ")
786 790 keymap.register("markrange", "%")
787 791 keymap.register("sortattrasc", "v")
788 792 keymap.register("sortattrdesc", "V")
789 793 keymap.register("goto", "g")
790 794 keymap.register("find", "f")
791 795 keymap.register("findbackwards", "b")
792 796 keymap.register("refresh", "r")
793 797 keymap.register("refreshfind", "R")
794 798
795 799 def __init__(self, *attrs):
796 800 """
797 801 Create a new browser. If ``attrs`` is not empty, it is the list
798 802 of attributes that will be displayed in the browser, otherwise
799 803 these will be determined by the objects on screen.
800 804 """
801 805 self.attrs = attrs
802 806
803 807 # Stack of browser levels
804 808 self.levels = []
805 809 # how many colums to scroll (Changes when accelerating)
806 810 self.stepx = 1.
807 811
808 812 # how many rows to scroll (Changes when accelerating)
809 813 self.stepy = 1.
810 814
811 815 # Beep on the edges of the data area? (Will be set to ``False``
812 816 # once the cursor hits the edge of the screen, so we don't get
813 817 # multiple beeps).
814 818 self._dobeep = True
815 819
816 820 # Cache for registered ``curses`` colors and styles.
817 821 self._styles = {}
818 822 self._colors = {}
819 823 self._maxcolor = 1
820 824
821 825 # How many header lines do we want to paint (the numbers of levels
822 826 # we have, but with an upper bound)
823 827 self._headerlines = 1
824 828
825 829 # Index of first header line
826 830 self._firstheaderline = 0
827 831
828 832 # curses window
829 833 self.scr = None
830 834 # report in the footer line (error, executed command etc.)
831 835 self._report = None
832 836
833 837 # value to be returned to the caller (set by commands)
834 838 self.returnvalue = None
835 839
836 840 # The mode the browser is in
837 841 # e.g. normal browsing or entering an argument for a command
838 842 self.mode = "default"
839 843
840 844 # set by the SIGWINCH signal handler
841 845 self.resized = False
842 846
843 847 def nextstepx(self, step):
844 848 """
845 849 Accelerate horizontally.
846 850 """
847 851 return max(1., min(step*self.acceleratex,
848 852 self.maxspeedx*self.levels[-1].mainsizex))
849 853
850 854 def nextstepy(self, step):
851 855 """
852 856 Accelerate vertically.
853 857 """
854 858 return max(1., min(step*self.acceleratey,
855 859 self.maxspeedy*self.levels[-1].mainsizey))
856 860
857 861 def getstyle(self, style):
858 862 """
859 863 Register the ``style`` with ``curses`` or get it from the cache,
860 864 if it has been registered before.
861 865 """
862 866 try:
863 867 return self._styles[style.fg, style.bg, style.attrs]
864 868 except KeyError:
865 869 attrs = 0
866 870 for b in astyle.A2CURSES:
867 871 if style.attrs & b:
868 872 attrs |= astyle.A2CURSES[b]
869 873 try:
870 874 color = self._colors[style.fg, style.bg]
871 875 except KeyError:
872 876 curses.init_pair(
873 877 self._maxcolor,
874 878 astyle.COLOR2CURSES[style.fg],
875 879 astyle.COLOR2CURSES[style.bg]
876 880 )
877 881 color = curses.color_pair(self._maxcolor)
878 882 self._colors[style.fg, style.bg] = color
879 883 self._maxcolor += 1
880 884 c = color | attrs
881 885 self._styles[style.fg, style.bg, style.attrs] = c
882 886 return c
883 887
884 888 def addstr(self, y, x, begx, endx, text, style):
885 889 """
886 890 A version of ``curses.addstr()`` that can handle ``x`` coordinates
887 891 that are outside the screen.
888 892 """
889 893 text2 = text[max(0, begx-x):max(0, endx-x)]
890 894 if text2:
891 895 self.scr.addstr(y, max(x, begx), text2, self.getstyle(style))
892 896 return len(text)
893 897
894 898 def addchr(self, y, x, begx, endx, c, l, style):
895 899 x0 = max(x, begx)
896 900 x1 = min(x+l, endx)
897 901 if x1>x0:
898 902 self.scr.addstr(y, x0, c*(x1-x0), self.getstyle(style))
899 903 return l
900 904
901 905 def _calcheaderlines(self, levels):
902 906 # Calculate how many headerlines do we have to display, if we have
903 907 # ``levels`` browser levels
904 908 if levels is None:
905 909 levels = len(self.levels)
906 910 self._headerlines = min(self.maxheaders, levels)
907 911 self._firstheaderline = levels-self._headerlines
908 912
909 913 def getstylehere(self, style):
910 914 """
911 915 Return a style for displaying the original style ``style``
912 916 in the row the cursor is on.
913 917 """
914 918 return astyle.Style(style.fg, astyle.COLOR_BLUE, style.attrs | astyle.A_BOLD)
915 919
916 920 def report(self, msg):
917 921 """
918 922 Store the message ``msg`` for display below the footer line. This
919 923 will be displayed as soon as the screen is redrawn.
920 924 """
921 925 self._report = msg
922 926
923 927 def enter(self, item, *attrs):
924 928 """
925 929 Enter the object ``item``. If ``attrs`` is specified, it will be used
926 930 as a fixed list of attributes to display.
927 931 """
928 932 if self.levels and item is self.levels[-1].input:
929 933 curses.beep()
930 934 self.report(CommandError("Recursion on input object"))
931 935 else:
932 936 oldlevels = len(self.levels)
933 937 self._calcheaderlines(oldlevels+1)
934 938 try:
935 939 level = _BrowserLevel(
936 940 self,
937 941 item,
938 942 self.scrsizey-1-self._headerlines-2,
939 943 *attrs
940 944 )
941 945 except (KeyboardInterrupt, SystemExit):
942 946 raise
943 947 except Exception, exc:
944 948 if not self.levels:
945 949 raise
946 950 self._calcheaderlines(oldlevels)
947 951 curses.beep()
948 952 self.report(exc)
949 953 else:
950 954 self.levels.append(level)
951 955
952 956 def startkeyboardinput(self, mode):
953 957 """
954 958 Enter mode ``mode``, which requires keyboard input.
955 959 """
956 960 self.mode = mode
957 961 self.prompts[mode].start()
958 962
959 963 def keylabel(self, keycode):
960 964 """
961 965 Return a pretty name for the ``curses`` key ``keycode`` (used in the
962 966 help screen and in reports about unassigned keys).
963 967 """
964 968 if keycode <= 0xff:
965 969 specialsnames = {
966 970 ord("\n"): "RETURN",
967 971 ord(" "): "SPACE",
968 972 ord("\t"): "TAB",
969 973 ord("\x7f"): "DELETE",
970 974 ord("\x08"): "BACKSPACE",
971 975 }
972 976 if keycode in specialsnames:
973 977 return specialsnames[keycode]
974 978 elif 0x00 < keycode < 0x20:
975 979 return "CTRL-%s" % chr(keycode + 64)
976 980 return repr(chr(keycode))
977 981 for name in dir(curses):
978 982 if name.startswith("KEY_") and getattr(curses, name) == keycode:
979 983 return name
980 984 return str(keycode)
981 985
982 986 def beep(self, force=False):
983 987 if force or self._dobeep:
984 988 curses.beep()
985 989 # don't beep again (as long as the same key is pressed)
986 990 self._dobeep = False
987 991
988 992 def cmd_up(self):
989 993 """
990 994 Move the cursor to the previous row.
991 995 """
992 996 level = self.levels[-1]
993 997 self.report("up")
994 998 level.moveto(level.curx, level.cury-self.stepy)
995 999
996 1000 def cmd_down(self):
997 1001 """
998 1002 Move the cursor to the next row.
999 1003 """
1000 1004 level = self.levels[-1]
1001 1005 self.report("down")
1002 1006 level.moveto(level.curx, level.cury+self.stepy)
1003 1007
1004 1008 def cmd_pageup(self):
1005 1009 """
1006 1010 Move the cursor up one page.
1007 1011 """
1008 1012 level = self.levels[-1]
1009 1013 self.report("page up")
1010 1014 level.moveto(level.curx, level.cury-level.mainsizey+self.pageoverlapy)
1011 1015
1012 1016 def cmd_pagedown(self):
1013 1017 """
1014 1018 Move the cursor down one page.
1015 1019 """
1016 1020 level = self.levels[-1]
1017 1021 self.report("page down")
1018 1022 level.moveto(level.curx, level.cury+level.mainsizey-self.pageoverlapy)
1019 1023
1020 1024 def cmd_left(self):
1021 1025 """
1022 1026 Move the cursor left.
1023 1027 """
1024 1028 level = self.levels[-1]
1025 1029 self.report("left")
1026 1030 level.moveto(level.curx-self.stepx, level.cury)
1027 1031
1028 1032 def cmd_right(self):
1029 1033 """
1030 1034 Move the cursor right.
1031 1035 """
1032 1036 level = self.levels[-1]
1033 1037 self.report("right")
1034 1038 level.moveto(level.curx+self.stepx, level.cury)
1035 1039
1036 1040 def cmd_home(self):
1037 1041 """
1038 1042 Move the cursor to the first column.
1039 1043 """
1040 1044 level = self.levels[-1]
1041 1045 self.report("home")
1042 1046 level.moveto(0, level.cury)
1043 1047
1044 1048 def cmd_end(self):
1045 1049 """
1046 1050 Move the cursor to the last column.
1047 1051 """
1048 1052 level = self.levels[-1]
1049 1053 self.report("end")
1050 1054 level.moveto(level.datasizex+level.mainsizey-self.pageoverlapx, level.cury)
1051 1055
1052 1056 def cmd_prevattr(self):
1053 1057 """
1054 1058 Move the cursor one attribute column to the left.
1055 1059 """
1056 1060 level = self.levels[-1]
1057 1061 if level.displayattr[0] is None or level.displayattr[0] == 0:
1058 1062 self.beep()
1059 1063 else:
1060 1064 self.report("prevattr")
1061 1065 pos = 0
1062 1066 for (i, attrname) in enumerate(level.displayattrs):
1063 1067 if i == level.displayattr[0]-1:
1064 1068 break
1065 1069 pos += level.colwidths[attrname] + 1
1066 1070 level.moveto(pos, level.cury)
1067 1071
1068 1072 def cmd_nextattr(self):
1069 1073 """
1070 1074 Move the cursor one attribute column to the right.
1071 1075 """
1072 1076 level = self.levels[-1]
1073 1077 if level.displayattr[0] is None or level.displayattr[0] == len(level.displayattrs)-1:
1074 1078 self.beep()
1075 1079 else:
1076 1080 self.report("nextattr")
1077 1081 pos = 0
1078 1082 for (i, attrname) in enumerate(level.displayattrs):
1079 1083 if i == level.displayattr[0]+1:
1080 1084 break
1081 1085 pos += level.colwidths[attrname] + 1
1082 1086 level.moveto(pos, level.cury)
1083 1087
1084 1088 def cmd_pick(self):
1085 1089 """
1086 1090 'Pick' the object under the cursor (i.e. the row the cursor is on).
1087 1091 This leaves the browser and returns the picked object to the caller.
1088 1092 (In IPython this object will be available as the ``_`` variable.)
1089 1093 """
1090 1094 level = self.levels[-1]
1091 1095 self.returnvalue = level.items[level.cury].item
1092 1096 return True
1093 1097
1094 1098 def cmd_pickattr(self):
1095 1099 """
1096 1100 'Pick' the attribute under the cursor (i.e. the row/column the
1097 1101 cursor is on).
1098 1102 """
1099 1103 level = self.levels[-1]
1100 1104 attr = level.displayattr[1]
1101 1105 if attr is ipipe.noitem:
1102 1106 curses.beep()
1103 1107 self.report(CommandError("no column under cursor"))
1104 1108 return
1105 1109 value = attr.value(level.items[level.cury].item)
1106 1110 if value is ipipe.noitem:
1107 1111 curses.beep()
1108 1112 self.report(AttributeError(attr.name()))
1109 1113 else:
1110 1114 self.returnvalue = value
1111 1115 return True
1112 1116
1113 1117 def cmd_pickallattrs(self):
1114 1118 """
1115 1119 Pick' the complete column under the cursor (i.e. the attribute under
1116 1120 the cursor) from all currently fetched objects. These attributes
1117 1121 will be returned as a list.
1118 1122 """
1119 1123 level = self.levels[-1]
1120 1124 attr = level.displayattr[1]
1121 1125 if attr is ipipe.noitem:
1122 1126 curses.beep()
1123 1127 self.report(CommandError("no column under cursor"))
1124 1128 return
1125 1129 result = []
1126 1130 for cache in level.items:
1127 1131 value = attr.value(cache.item)
1128 1132 if value is not ipipe.noitem:
1129 1133 result.append(value)
1130 1134 self.returnvalue = result
1131 1135 return True
1132 1136
1133 1137 def cmd_pickmarked(self):
1134 1138 """
1135 1139 'Pick' marked objects. Marked objects will be returned as a list.
1136 1140 """
1137 1141 level = self.levels[-1]
1138 1142 self.returnvalue = [cache.item for cache in level.items if cache.marked]
1139 1143 return True
1140 1144
1141 1145 def cmd_pickmarkedattr(self):
1142 1146 """
1143 1147 'Pick' the attribute under the cursor from all marked objects
1144 1148 (This returns a list).
1145 1149 """
1146 1150
1147 1151 level = self.levels[-1]
1148 1152 attr = level.displayattr[1]
1149 1153 if attr is ipipe.noitem:
1150 1154 curses.beep()
1151 1155 self.report(CommandError("no column under cursor"))
1152 1156 return
1153 1157 result = []
1154 1158 for cache in level.items:
1155 1159 if cache.marked:
1156 1160 value = attr.value(cache.item)
1157 1161 if value is not ipipe.noitem:
1158 1162 result.append(value)
1159 1163 self.returnvalue = result
1160 1164 return True
1161 1165
1166 def cmd_pickinput(self):
1167 """
1168 Use the object under the cursor (i.e. the row the cursor is on) as
1169 the next input line. This leaves the browser and puts the picked object
1170 in the input.
1171 """
1172 level = self.levels[-1]
1173 value = level.items[level.cury].item
1174 self.returnvalue = None
1175 api = ipapi.get()
1176 api.set_next_input(str(value))
1177 return True
1178
1179 def cmd_pickinputattr(self):
1180 """
1181 Use the attribute under the cursor i.e. the row/column the cursor is on)
1182 as the next input line. This leaves the browser and puts the picked
1183 object in the input.
1184 """
1185 level = self.levels[-1]
1186 attr = level.displayattr[1]
1187 if attr is ipipe.noitem:
1188 curses.beep()
1189 self.report(CommandError("no column under cursor"))
1190 return
1191 value = attr.value(level.items[level.cury].item)
1192 if value is ipipe.noitem:
1193 curses.beep()
1194 self.report(AttributeError(attr.name()))
1195 self.returnvalue = None
1196 api = ipapi.get()
1197 api.set_next_input(str(value))
1198 return True
1199
1162 1200 def cmd_markrange(self):
1163 1201 """
1164 1202 Mark all objects from the last marked object before the current cursor
1165 1203 position to the cursor position.
1166 1204 """
1167 1205 level = self.levels[-1]
1168 1206 self.report("markrange")
1169 1207 start = None
1170 1208 if level.items:
1171 1209 for i in xrange(level.cury, -1, -1):
1172 1210 if level.items[i].marked:
1173 1211 start = i
1174 1212 break
1175 1213 if start is None:
1176 1214 self.report(CommandError("no mark before cursor"))
1177 1215 curses.beep()
1178 1216 else:
1179 1217 for i in xrange(start, level.cury+1):
1180 1218 cache = level.items[i]
1181 1219 if not cache.marked:
1182 1220 cache.marked = True
1183 1221 level.marked += 1
1184 1222
1185 1223 def cmd_enter(self):
1186 1224 """
1187 1225 Enter the object under the cursor. (what this mean depends on the object
1188 1226 itself (i.e. how it implements iteration). This opens a new browser 'level'.
1189 1227 """
1190 1228 level = self.levels[-1]
1191 1229 try:
1192 1230 item = level.items[level.cury].item
1193 1231 except IndexError:
1194 1232 self.report(CommandError("No object"))
1195 1233 curses.beep()
1196 1234 else:
1197 1235 self.report("entering object...")
1198 1236 self.enter(item)
1199 1237
1200 1238 def cmd_leave(self):
1201 1239 """
1202 1240 Leave the current browser level and go back to the previous one.
1203 1241 """
1204 1242 self.report("leave")
1205 1243 if len(self.levels) > 1:
1206 1244 self._calcheaderlines(len(self.levels)-1)
1207 1245 self.levels.pop(-1)
1208 1246 else:
1209 1247 self.report(CommandError("This is the last level"))
1210 1248 curses.beep()
1211 1249
1212 1250 def cmd_enterattr(self):
1213 1251 """
1214 1252 Enter the attribute under the cursor.
1215 1253 """
1216 1254 level = self.levels[-1]
1217 1255 attr = level.displayattr[1]
1218 1256 if attr is ipipe.noitem:
1219 1257 curses.beep()
1220 1258 self.report(CommandError("no column under cursor"))
1221 1259 return
1222 1260 try:
1223 1261 item = level.items[level.cury].item
1224 1262 except IndexError:
1225 1263 self.report(CommandError("No object"))
1226 1264 curses.beep()
1227 1265 else:
1228 1266 value = attr.value(item)
1229 1267 name = attr.name()
1230 1268 if value is ipipe.noitem:
1231 1269 self.report(AttributeError(name))
1232 1270 else:
1233 1271 self.report("entering object attribute %s..." % name)
1234 1272 self.enter(value)
1235 1273
1236 1274 def cmd_detail(self):
1237 1275 """
1238 1276 Show a detail view of the object under the cursor. This shows the
1239 1277 name, type, doc string and value of the object attributes (and it
1240 1278 might show more attributes than in the list view, depending on
1241 1279 the object).
1242 1280 """
1243 1281 level = self.levels[-1]
1244 1282 try:
1245 1283 item = level.items[level.cury].item
1246 1284 except IndexError:
1247 1285 self.report(CommandError("No object"))
1248 1286 curses.beep()
1249 1287 else:
1250 1288 self.report("entering detail view for object...")
1251 1289 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
1252 1290 self.enter(attrs)
1253 1291
1254 1292 def cmd_detailattr(self):
1255 1293 """
1256 1294 Show a detail view of the attribute under the cursor.
1257 1295 """
1258 1296 level = self.levels[-1]
1259 1297 attr = level.displayattr[1]
1260 1298 if attr is ipipe.noitem:
1261 1299 curses.beep()
1262 1300 self.report(CommandError("no attribute"))
1263 1301 return
1264 1302 try:
1265 1303 item = level.items[level.cury].item
1266 1304 except IndexError:
1267 1305 self.report(CommandError("No object"))
1268 1306 curses.beep()
1269 1307 else:
1270 1308 try:
1271 1309 item = attr.value(item)
1272 1310 except (KeyboardInterrupt, SystemExit):
1273 1311 raise
1274 1312 except Exception, exc:
1275 1313 self.report(exc)
1276 1314 else:
1277 1315 self.report("entering detail view for attribute %s..." % attr.name())
1278 1316 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
1279 1317 self.enter(attrs)
1280 1318
1281 1319 def cmd_tooglemark(self):
1282 1320 """
1283 1321 Mark/unmark the object under the cursor. Marked objects have a '!'
1284 1322 after the row number).
1285 1323 """
1286 1324 level = self.levels[-1]
1287 1325 self.report("toggle mark")
1288 1326 try:
1289 1327 item = level.items[level.cury]
1290 1328 except IndexError: # no items?
1291 1329 pass
1292 1330 else:
1293 1331 if item.marked:
1294 1332 item.marked = False
1295 1333 level.marked -= 1
1296 1334 else:
1297 1335 item.marked = True
1298 1336 level.marked += 1
1299 1337
1300 1338 def cmd_sortattrasc(self):
1301 1339 """
1302 1340 Sort the objects (in ascending order) using the attribute under
1303 1341 the cursor as the sort key.
1304 1342 """
1305 1343 level = self.levels[-1]
1306 1344 attr = level.displayattr[1]
1307 1345 if attr is ipipe.noitem:
1308 1346 curses.beep()
1309 1347 self.report(CommandError("no column under cursor"))
1310 1348 return
1311 1349 self.report("sort by %s (ascending)" % attr.name())
1312 1350 def key(item):
1313 1351 try:
1314 1352 return attr.value(item)
1315 1353 except (KeyboardInterrupt, SystemExit):
1316 1354 raise
1317 1355 except Exception:
1318 1356 return None
1319 1357 level.sort(key)
1320 1358
1321 1359 def cmd_sortattrdesc(self):
1322 1360 """
1323 1361 Sort the objects (in descending order) using the attribute under
1324 1362 the cursor as the sort key.
1325 1363 """
1326 1364 level = self.levels[-1]
1327 1365 attr = level.displayattr[1]
1328 1366 if attr is ipipe.noitem:
1329 1367 curses.beep()
1330 1368 self.report(CommandError("no column under cursor"))
1331 1369 return
1332 1370 self.report("sort by %s (descending)" % attr.name())
1333 1371 def key(item):
1334 1372 try:
1335 1373 return attr.value(item)
1336 1374 except (KeyboardInterrupt, SystemExit):
1337 1375 raise
1338 1376 except Exception:
1339 1377 return None
1340 1378 level.sort(key, reverse=True)
1341 1379
1342 1380 def cmd_hideattr(self):
1343 1381 """
1344 1382 Hide the attribute under the cursor.
1345 1383 """
1346 1384 level = self.levels[-1]
1347 1385 if level.displayattr[0] is None:
1348 1386 self.beep()
1349 1387 else:
1350 1388 self.report("hideattr")
1351 1389 level.hiddenattrs.add(level.displayattr[1])
1352 1390 level.moveto(level.curx, level.cury, refresh=True)
1353 1391
1354 1392 def cmd_unhideattrs(self):
1355 1393 """
1356 1394 Make all attributes visible again.
1357 1395 """
1358 1396 level = self.levels[-1]
1359 1397 self.report("unhideattrs")
1360 1398 level.hiddenattrs.clear()
1361 1399 level.moveto(level.curx, level.cury, refresh=True)
1362 1400
1363 1401 def cmd_goto(self):
1364 1402 """
1365 1403 Jump to a row. The row number can be entered at the
1366 1404 bottom of the screen.
1367 1405 """
1368 1406 self.startkeyboardinput("goto")
1369 1407
1370 1408 def cmd_find(self):
1371 1409 """
1372 1410 Search forward for a row. The search condition can be entered at the
1373 1411 bottom of the screen.
1374 1412 """
1375 1413 self.startkeyboardinput("find")
1376 1414
1377 1415 def cmd_findbackwards(self):
1378 1416 """
1379 1417 Search backward for a row. The search condition can be entered at the
1380 1418 bottom of the screen.
1381 1419 """
1382 1420 self.startkeyboardinput("findbackwards")
1383 1421
1384 1422 def cmd_refresh(self):
1385 1423 """
1386 1424 Refreshes the display by restarting the iterator.
1387 1425 """
1388 1426 level = self.levels[-1]
1389 1427 self.report("refresh")
1390 1428 level.refresh()
1391 1429
1392 1430 def cmd_refreshfind(self):
1393 1431 """
1394 1432 Refreshes the display by restarting the iterator and goes back to the
1395 1433 same object the cursor was on before restarting (if this object can't be
1396 1434 found the cursor jumps back to the first object).
1397 1435 """
1398 1436 level = self.levels[-1]
1399 1437 self.report("refreshfind")
1400 1438 level.refreshfind()
1401 1439
1402 1440 def cmd_help(self):
1403 1441 """
1404 1442 Opens the help screen as a new browser level, describing keyboard
1405 1443 shortcuts.
1406 1444 """
1407 1445 for level in self.levels:
1408 1446 if isinstance(level.input, _BrowserHelp):
1409 1447 curses.beep()
1410 1448 self.report(CommandError("help already active"))
1411 1449 return
1412 1450
1413 1451 self.enter(_BrowserHelp(self))
1414 1452
1415 1453 def cmd_quit(self):
1416 1454 """
1417 1455 Quit the browser and return to the IPython prompt.
1418 1456 """
1419 1457 self.returnvalue = None
1420 1458 return True
1421 1459
1422 1460 def sigwinchhandler(self, signal, frame):
1423 1461 self.resized = True
1424 1462
1425 1463 def _dodisplay(self, scr):
1426 1464 """
1427 1465 This method is the workhorse of the browser. It handles screen
1428 1466 drawing and the keyboard.
1429 1467 """
1430 1468 self.scr = scr
1431 1469 curses.halfdelay(1)
1432 1470 footery = 2
1433 1471
1434 1472 keys = []
1435 1473 for cmd in ("quit", "help"):
1436 1474 key = self.keymap.findkey(cmd, None)
1437 1475 if key is not None:
1438 1476 keys.append("%s=%s" % (self.keylabel(key), cmd))
1439 1477 helpmsg = " | %s" % " ".join(keys)
1440 1478
1441 1479 scr.clear()
1442 1480 msg = "Fetching first batch of objects..."
1443 1481 (self.scrsizey, self.scrsizex) = scr.getmaxyx()
1444 1482 scr.addstr(self.scrsizey//2, (self.scrsizex-len(msg))//2, msg)
1445 1483 scr.refresh()
1446 1484
1447 1485 lastc = -1
1448 1486
1449 1487 self.levels = []
1450 1488 # enter the first level
1451 1489 self.enter(self.input, *self.attrs)
1452 1490
1453 1491 self._calcheaderlines(None)
1454 1492
1455 1493 while True:
1456 1494 level = self.levels[-1]
1457 1495 (self.scrsizey, self.scrsizex) = scr.getmaxyx()
1458 1496 level.mainsizey = self.scrsizey-1-self._headerlines-footery
1459 1497
1460 1498 # Paint object header
1461 1499 for i in xrange(self._firstheaderline, self._firstheaderline+self._headerlines):
1462 1500 lv = self.levels[i]
1463 1501 posx = 0
1464 1502 posy = i-self._firstheaderline
1465 1503 endx = self.scrsizex
1466 1504 if i: # not the first level
1467 1505 msg = " (%d/%d" % (self.levels[i-1].cury, len(self.levels[i-1].items))
1468 1506 if not self.levels[i-1].exhausted:
1469 1507 msg += "+"
1470 1508 msg += ") "
1471 1509 endx -= len(msg)+1
1472 1510 posx += self.addstr(posy, posx, 0, endx, " ibrowse #%d: " % i, self.style_objheadertext)
1473 1511 for (style, text) in lv.header:
1474 1512 posx += self.addstr(posy, posx, 0, endx, text, self.style_objheaderobject)
1475 1513 if posx >= endx:
1476 1514 break
1477 1515 if i:
1478 1516 posx += self.addstr(posy, posx, 0, self.scrsizex, msg, self.style_objheadernumber)
1479 1517 posx += self.addchr(posy, posx, 0, self.scrsizex, " ", self.scrsizex-posx, self.style_objheadernumber)
1480 1518
1481 1519 if not level.items:
1482 1520 self.addchr(self._headerlines, 0, 0, self.scrsizex, " ", self.scrsizex, self.style_colheader)
1483 1521 self.addstr(self._headerlines+1, 0, 0, self.scrsizex, " <empty>", astyle.style_error)
1484 1522 scr.clrtobot()
1485 1523 else:
1486 1524 # Paint column headers
1487 1525 scr.move(self._headerlines, 0)
1488 1526 scr.addstr(" %*s " % (level.numbersizex, "#"), self.getstyle(self.style_colheader))
1489 1527 scr.addstr(self.headersepchar, self.getstyle(self.style_colheadersep))
1490 1528 begx = level.numbersizex+3
1491 1529 posx = begx-level.datastartx
1492 1530 for attr in level.displayattrs:
1493 1531 attrname = attr.name()
1494 1532 cwidth = level.colwidths[attr]
1495 1533 header = attrname.ljust(cwidth)
1496 1534 if attr is level.displayattr[1]:
1497 1535 style = self.style_colheaderhere
1498 1536 else:
1499 1537 style = self.style_colheader
1500 1538 posx += self.addstr(self._headerlines, posx, begx, self.scrsizex, header, style)
1501 1539 posx += self.addstr(self._headerlines, posx, begx, self.scrsizex, self.headersepchar, self.style_colheadersep)
1502 1540 if posx >= self.scrsizex:
1503 1541 break
1504 1542 else:
1505 1543 scr.addstr(" "*(self.scrsizex-posx), self.getstyle(self.style_colheader))
1506 1544
1507 1545 # Paint rows
1508 1546 posy = self._headerlines+1+level.datastarty
1509 1547 for i in xrange(level.datastarty, min(level.datastarty+level.mainsizey, len(level.items))):
1510 1548 cache = level.items[i]
1511 1549 if i == level.cury:
1512 1550 style = self.style_numberhere
1513 1551 else:
1514 1552 style = self.style_number
1515 1553
1516 1554 posy = self._headerlines+1+i-level.datastarty
1517 1555 posx = begx-level.datastartx
1518 1556
1519 1557 scr.move(posy, 0)
1520 1558 scr.addstr(" %*d%s" % (level.numbersizex, i, " !"[cache.marked]), self.getstyle(style))
1521 1559 scr.addstr(self.headersepchar, self.getstyle(self.style_sep))
1522 1560
1523 1561 for attrname in level.displayattrs:
1524 1562 cwidth = level.colwidths[attrname]
1525 1563 try:
1526 1564 (align, length, parts) = level.displayrows[i-level.datastarty][attrname]
1527 1565 except KeyError:
1528 1566 align = 2
1529 1567 style = astyle.style_nodata
1530 1568 if i == level.cury:
1531 1569 style = self.getstylehere(style)
1532 1570 padstyle = self.style_datapad
1533 1571 sepstyle = self.style_sep
1534 1572 if i == level.cury:
1535 1573 padstyle = self.getstylehere(padstyle)
1536 1574 sepstyle = self.getstylehere(sepstyle)
1537 1575 if align == 2:
1538 1576 posx += self.addchr(posy, posx, begx, self.scrsizex, self.nodatachar, cwidth, style)
1539 1577 else:
1540 1578 if align == 1:
1541 1579 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle)
1542 1580 elif align == 0:
1543 1581 pad1 = (cwidth-length)//2
1544 1582 pad2 = cwidth-length-len(pad1)
1545 1583 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad1, padstyle)
1546 1584 for (style, text) in parts:
1547 1585 if i == level.cury:
1548 1586 style = self.getstylehere(style)
1549 1587 posx += self.addstr(posy, posx, begx, self.scrsizex, text, style)
1550 1588 if posx >= self.scrsizex:
1551 1589 break
1552 1590 if align == -1:
1553 1591 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle)
1554 1592 elif align == 0:
1555 1593 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad2, padstyle)
1556 1594 posx += self.addstr(posy, posx, begx, self.scrsizex, self.datasepchar, sepstyle)
1557 1595 else:
1558 1596 scr.clrtoeol()
1559 1597
1560 1598 # Add blank row headers for the rest of the screen
1561 1599 for posy in xrange(posy+1, self.scrsizey-2):
1562 1600 scr.addstr(posy, 0, " " * (level.numbersizex+2), self.getstyle(self.style_colheader))
1563 1601 scr.clrtoeol()
1564 1602
1565 1603 posy = self.scrsizey-footery
1566 1604 # Display footer
1567 1605 scr.addstr(posy, 0, " "*self.scrsizex, self.getstyle(self.style_footer))
1568 1606
1569 1607 if level.exhausted:
1570 1608 flag = ""
1571 1609 else:
1572 1610 flag = "+"
1573 1611
1574 1612 endx = self.scrsizex-len(helpmsg)-1
1575 1613 scr.addstr(posy, endx, helpmsg, self.getstyle(self.style_footer))
1576 1614
1577 1615 posx = 0
1578 1616 msg = " %d%s objects (%d marked): " % (len(level.items), flag, level.marked)
1579 1617 posx += self.addstr(posy, posx, 0, endx, msg, self.style_footer)
1580 1618 try:
1581 1619 item = level.items[level.cury].item
1582 1620 except IndexError: # empty
1583 1621 pass
1584 1622 else:
1585 1623 for (nostyle, text) in ipipe.xrepr(item, "footer"):
1586 1624 if not isinstance(nostyle, int):
1587 1625 posx += self.addstr(posy, posx, 0, endx, text, self.style_footer)
1588 1626 if posx >= endx:
1589 1627 break
1590 1628
1591 1629 attrstyle = [(astyle.style_default, "no attribute")]
1592 1630 attr = level.displayattr[1]
1593 1631 if attr is not ipipe.noitem and not isinstance(attr, ipipe.SelfDescriptor):
1594 1632 posx += self.addstr(posy, posx, 0, endx, " | ", self.style_footer)
1595 1633 posx += self.addstr(posy, posx, 0, endx, attr.name(), self.style_footer)
1596 1634 posx += self.addstr(posy, posx, 0, endx, ": ", self.style_footer)
1597 1635 try:
1598 1636 value = attr.value(item)
1599 1637 except (SystemExit, KeyboardInterrupt):
1600 1638 raise
1601 1639 except Exception, exc:
1602 1640 value = exc
1603 1641 if value is not ipipe.noitem:
1604 1642 attrstyle = ipipe.xrepr(value, "footer")
1605 1643 for (nostyle, text) in attrstyle:
1606 1644 if not isinstance(nostyle, int):
1607 1645 posx += self.addstr(posy, posx, 0, endx, text, self.style_footer)
1608 1646 if posx >= endx:
1609 1647 break
1610 1648
1611 1649 try:
1612 1650 # Display input prompt
1613 1651 if self.mode in self.prompts:
1614 1652 history = self.prompts[self.mode]
1615 1653 posx = 0
1616 1654 posy = self.scrsizey-1
1617 1655 posx += self.addstr(posy, posx, 0, endx, history.prompt, astyle.style_default)
1618 1656 posx += self.addstr(posy, posx, 0, endx, " [", astyle.style_default)
1619 1657 if history.cury==-1:
1620 1658 text = "new"
1621 1659 else:
1622 1660 text = str(history.cury+1)
1623 1661 posx += self.addstr(posy, posx, 0, endx, text, astyle.style_type_number)
1624 1662 if history.history:
1625 1663 posx += self.addstr(posy, posx, 0, endx, "/", astyle.style_default)
1626 1664 posx += self.addstr(posy, posx, 0, endx, str(len(history.history)), astyle.style_type_number)
1627 1665 posx += self.addstr(posy, posx, 0, endx, "]: ", astyle.style_default)
1628 1666 inputstartx = posx
1629 1667 posx += self.addstr(posy, posx, 0, endx, history.input, astyle.style_default)
1630 1668 # Display report
1631 1669 else:
1632 1670 if self._report is not None:
1633 1671 if isinstance(self._report, Exception):
1634 1672 style = self.getstyle(astyle.style_error)
1635 1673 if self._report.__class__.__module__ == "exceptions":
1636 1674 msg = "%s: %s" % \
1637 1675 (self._report.__class__.__name__, self._report)
1638 1676 else:
1639 1677 msg = "%s.%s: %s" % \
1640 1678 (self._report.__class__.__module__,
1641 1679 self._report.__class__.__name__, self._report)
1642 1680 else:
1643 1681 style = self.getstyle(self.style_report)
1644 1682 msg = self._report
1645 1683 scr.addstr(self.scrsizey-1, 0, msg[:self.scrsizex], style)
1646 1684 self._report = None
1647 1685 else:
1648 1686 scr.move(self.scrsizey-1, 0)
1649 1687 except curses.error:
1650 1688 # Protect against errors from writing to the last line
1651 1689 pass
1652 1690 scr.clrtoeol()
1653 1691
1654 1692 # Position cursor
1655 1693 if self.mode in self.prompts:
1656 1694 history = self.prompts[self.mode]
1657 1695 scr.move(self.scrsizey-1, inputstartx+history.curx)
1658 1696 else:
1659 1697 scr.move(
1660 1698 1+self._headerlines+level.cury-level.datastarty,
1661 1699 level.numbersizex+3+level.curx-level.datastartx
1662 1700 )
1663 1701 scr.refresh()
1664 1702
1665 1703 # Check keyboard
1666 1704 while True:
1667 1705 c = scr.getch()
1668 1706 if self.resized:
1669 1707 size = fcntl.ioctl(0, tty.TIOCGWINSZ, "12345678")
1670 1708 size = struct.unpack("4H", size)
1671 1709 oldsize = scr.getmaxyx()
1672 1710 scr.erase()
1673 1711 curses.resize_term(size[0], size[1])
1674 1712 newsize = scr.getmaxyx()
1675 1713 scr.erase()
1676 1714 for l in self.levels:
1677 1715 l.mainsizey += newsize[0]-oldsize[0]
1678 1716 l.moveto(l.curx, l.cury, refresh=True)
1679 1717 scr.refresh()
1680 1718 self.resized = False
1681 1719 break # Redisplay
1682 1720 if self.mode in self.prompts:
1683 1721 if self.prompts[self.mode].handlekey(self, c):
1684 1722 break # Redisplay
1685 1723 else:
1686 1724 # if no key is pressed slow down and beep again
1687 1725 if c == -1:
1688 1726 self.stepx = 1.
1689 1727 self.stepy = 1.
1690 1728 self._dobeep = True
1691 1729 else:
1692 1730 # if a different key was pressed slow down and beep too
1693 1731 if c != lastc:
1694 1732 lastc = c
1695 1733 self.stepx = 1.
1696 1734 self.stepy = 1.
1697 1735 self._dobeep = True
1698 1736 cmdname = self.keymap.get(c, None)
1699 1737 if cmdname is None:
1700 1738 self.report(
1701 1739 UnassignedKeyError("Unassigned key %s" %
1702 1740 self.keylabel(c)))
1703 1741 else:
1704 1742 cmdfunc = getattr(self, "cmd_%s" % cmdname, None)
1705 1743 if cmdfunc is None:
1706 1744 self.report(
1707 1745 UnknownCommandError("Unknown command %r" %
1708 1746 (cmdname,)))
1709 1747 elif cmdfunc():
1710 1748 returnvalue = self.returnvalue
1711 1749 self.returnvalue = None
1712 1750 return returnvalue
1713 1751 self.stepx = self.nextstepx(self.stepx)
1714 1752 self.stepy = self.nextstepy(self.stepy)
1715 1753 curses.flushinp() # get rid of type ahead
1716 1754 break # Redisplay
1717 1755 self.scr = None
1718 1756
1719 1757 def display(self):
1720 1758 if hasattr(curses, "resize_term"):
1721 1759 oldhandler = signal.signal(signal.SIGWINCH, self.sigwinchhandler)
1722 1760 try:
1723 1761 return curses.wrapper(self._dodisplay)
1724 1762 finally:
1725 1763 signal.signal(signal.SIGWINCH, oldhandler)
1726 1764 else:
1727 1765 return curses.wrapper(self._dodisplay)
@@ -1,2153 +1,2175 b''
1 1 # -*- coding: iso-8859-1 -*-
2 2
3 3 """
4 4 ``ipipe`` provides classes to be used in an interactive Python session. Doing a
5 5 ``from ipipe import *`` is the preferred way to do this. The name of all
6 6 objects imported this way starts with ``i`` to minimize collisions.
7 7
8 8 ``ipipe`` supports "pipeline expressions", which is something resembling Unix
9 9 pipes. An example is:
10 10
11 11 >>> ienv | isort("key.lower()")
12 12
13 13 This gives a listing of all environment variables sorted by name.
14 14
15 15
16 16 There are three types of objects in a pipeline expression:
17 17
18 18 * ``Table``s: These objects produce items. Examples are ``ils`` (listing the
19 19 current directory, ``ienv`` (listing environment variables), ``ipwd`` (listing
20 20 user accounts) and ``igrp`` (listing user groups). A ``Table`` must be the
21 21 first object in a pipe expression.
22 22
23 23 * ``Pipe``s: These objects sit in the middle of a pipe expression. They
24 24 transform the input in some way (e.g. filtering or sorting it). Examples are:
25 25 ``ifilter`` (which filters the input pipe), ``isort`` (which sorts the input
26 26 pipe) and ``ieval`` (which evaluates a function or expression for each object
27 27 in the input pipe).
28 28
29 29 * ``Display``s: These objects can be put as the last object in a pipeline
30 30 expression. There are responsible for displaying the result of the pipeline
31 31 expression. If a pipeline expression doesn't end in a display object a default
32 32 display objects will be used. One example is ``ibrowse`` which is a ``curses``
33 33 based browser.
34 34
35 35
36 36 Adding support for pipeline expressions to your own objects can be done through
37 37 three extensions points (all of them optional):
38 38
39 39 * An object that will be displayed as a row by a ``Display`` object should
40 40 implement the method ``__xattrs__(self, mode)`` method or register an
41 41 implementation of the generic function ``xattrs``. For more info see ``xattrs``.
42 42
43 43 * When an object ``foo`` is displayed by a ``Display`` object, the generic
44 44 function ``xrepr`` is used.
45 45
46 46 * Objects that can be iterated by ``Pipe``s must iterable. For special cases,
47 47 where iteration for display is different than the normal iteration a special
48 48 implementation can be registered with the generic function ``xiter``. This makes
49 49 it possible to use dictionaries and modules in pipeline expressions, for example:
50 50
51 51 >>> import sys
52 52 >>> sys | ifilter("isinstance(value, int)") | idump
53 53 key |value
54 54 api_version| 1012
55 55 dllhandle | 503316480
56 56 hexversion | 33817328
57 57 maxint |2147483647
58 58 maxunicode | 65535
59 59 >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()")
60 60 ...
61 61
62 62 Note: The expression strings passed to ``ifilter()`` and ``isort()`` can
63 63 refer to the object to be filtered or sorted via the variable ``_`` and to any
64 64 of the attributes of the object, i.e.:
65 65
66 66 >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()")
67 67
68 68 does the same as
69 69
70 70 >>> sys.modules | ifilter("value is not None") | isort("key.lower()")
71 71
72 72 In addition to expression strings, it's possible to pass callables (taking
73 73 the object as an argument) to ``ifilter()``, ``isort()`` and ``ieval()``:
74 74
75 75 >>> sys | ifilter(lambda _:isinstance(_.value, int)) \
76 76 ... | ieval(lambda _: (_.key, hex(_.value))) | idump
77 77 0 |1
78 78 api_version|0x3f4
79 79 dllhandle |0x1e000000
80 80 hexversion |0x20402f0
81 81 maxint |0x7fffffff
82 82 maxunicode |0xffff
83 83 """
84 84
85 85 import sys, os, os.path, stat, glob, new, csv, datetime, types
86 86 import itertools, mimetypes
87 87
88 88 try: # Python 2.3 compatibility
89 89 import collections
90 90 except ImportError:
91 91 deque = list
92 92 else:
93 93 deque = collections.deque
94 94
95 95 try: # Python 2.3 compatibility
96 96 set
97 97 except NameError:
98 98 import sets
99 99 set = sets.Set
100 100
101 101 try: # Python 2.3 compatibility
102 102 sorted
103 103 except NameError:
104 104 def sorted(iterator, key=None, reverse=False):
105 105 items = list(iterator)
106 106 if key is not None:
107 107 items.sort(lambda i1, i2: cmp(key(i1), key(i2)))
108 108 else:
109 109 items.sort()
110 110 if reverse:
111 111 items.reverse()
112 112 return items
113 113
114 114 try:
115 115 import pwd
116 116 except ImportError:
117 117 pwd = None
118 118
119 119 try:
120 120 import grp
121 121 except ImportError:
122 122 grp = None
123 123
124 124 from IPython.external import simplegeneric
125 125
126 126 import path
127 127 try:
128 128 from IPython import genutils, ipapi
129 129 except ImportError:
130 130 genutils = None
131 131 ipapi = None
132 132
133 133 import astyle
134 134
135 135
136 136 __all__ = [
137 137 "ifile", "ils", "iglob", "iwalk", "ipwdentry", "ipwd", "igrpentry", "igrp",
138 "icsv", "ix", "ichain", "isort", "ifilter", "ieval", "ienum", "ienv",
139 "idump", "iless"
138 "icsv", "ix", "ichain", "isort", "ifilter", "ieval", "ienum",
139 "ienv", "ihist", "idump", "iless"
140 140 ]
141 141
142 142
143 143 os.stat_float_times(True) # enable microseconds
144 144
145 145
146 146 class AttrNamespace(object):
147 147 """
148 148 Helper class that is used for providing a namespace for evaluating
149 149 expressions containing attribute names of an object.
150 150 """
151 151 def __init__(self, wrapped):
152 152 self.wrapped = wrapped
153 153
154 154 def __getitem__(self, name):
155 155 if name == "_":
156 156 return self.wrapped
157 157 try:
158 158 return getattr(self.wrapped, name)
159 159 except AttributeError:
160 160 raise KeyError(name)
161 161
162 162 # Python 2.3 compatibility
163 163 # use eval workaround to find out which names are used in the
164 164 # eval string and put them into the locals. This works for most
165 165 # normal uses case, bizarre ones like accessing the locals()
166 166 # will fail
167 167 try:
168 168 eval("_", None, AttrNamespace(None))
169 169 except TypeError:
170 170 real_eval = eval
171 171 def eval(codestring, _globals, _locals):
172 172 """
173 173 eval(source[, globals[, locals]]) -> value
174 174
175 175 Evaluate the source in the context of globals and locals.
176 176 The source may be a string representing a Python expression
177 177 or a code object as returned by compile().
178 178 The globals must be a dictionary and locals can be any mappping.
179 179
180 180 This function is a workaround for the shortcomings of
181 181 Python 2.3's eval.
182 182 """
183 183
184 184 if isinstance(codestring, basestring):
185 185 code = compile(codestring, "_eval", "eval")
186 186 else:
187 187 code = codestring
188 188 newlocals = {}
189 189 for name in code.co_names:
190 190 try:
191 191 newlocals[name] = _locals[name]
192 192 except KeyError:
193 193 pass
194 194 return real_eval(code, _globals, newlocals)
195 195
196 196
197 197 noitem = object()
198 198
199 199
200 200 def item(iterator, index, default=noitem):
201 201 """
202 202 Return the ``index``th item from the iterator ``iterator``.
203 203 ``index`` must be an integer (negative integers are relative to the
204 204 end (i.e. the last items produced by the iterator)).
205 205
206 206 If ``default`` is given, this will be the default value when
207 207 the iterator doesn't contain an item at this position. Otherwise an
208 208 ``IndexError`` will be raised.
209 209
210 210 Note that using this function will partially or totally exhaust the
211 211 iterator.
212 212 """
213 213 i = index
214 214 if i>=0:
215 215 for item in iterator:
216 216 if not i:
217 217 return item
218 218 i -= 1
219 219 else:
220 220 i = -index
221 221 cache = deque()
222 222 for item in iterator:
223 223 cache.append(item)
224 224 if len(cache)>i:
225 225 cache.popleft()
226 226 if len(cache)==i:
227 227 return cache.popleft()
228 228 if default is noitem:
229 229 raise IndexError(index)
230 230 else:
231 231 return default
232 232
233 233
234 234 def getglobals(g):
235 235 """
236 236 Return the global namespace that is used for expression strings in
237 237 ``ifilter`` and others. This is ``g`` or (if ``g`` is ``None``) IPython's
238 238 user namespace.
239 239 """
240 240 if g is None:
241 241 if ipapi is not None:
242 242 api = ipapi.get()
243 243 if api is not None:
244 244 return api.user_ns
245 245 return globals()
246 246 return g
247 247
248 248
249 249 class Descriptor(object):
250 250 """
251 251 A ``Descriptor`` object is used for describing the attributes of objects.
252 252 """
253 253 def __hash__(self):
254 254 return hash(self.__class__) ^ hash(self.key())
255 255
256 256 def __eq__(self, other):
257 257 return self.__class__ is other.__class__ and self.key() == other.key()
258 258
259 259 def __ne__(self, other):
260 260 return self.__class__ is not other.__class__ or self.key() != other.key()
261 261
262 262 def key(self):
263 263 pass
264 264
265 265 def name(self):
266 266 """
267 267 Return the name of this attribute for display by a ``Display`` object
268 268 (e.g. as a column title).
269 269 """
270 270 key = self.key()
271 271 if key is None:
272 272 return "_"
273 273 return str(key)
274 274
275 275 def attrtype(self, obj):
276 276 """
277 277 Return the type of this attribute (i.e. something like "attribute" or
278 278 "method").
279 279 """
280 280
281 281 def valuetype(self, obj):
282 282 """
283 283 Return the type of this attribute value of the object ``obj``.
284 284 """
285 285
286 286 def value(self, obj):
287 287 """
288 288 Return the value of this attribute of the object ``obj``.
289 289 """
290 290
291 291 def doc(self, obj):
292 292 """
293 293 Return the documentation for this attribute.
294 294 """
295 295
296 296 def shortdoc(self, obj):
297 297 """
298 298 Return a short documentation for this attribute (defaulting to the
299 299 first line).
300 300 """
301 301 doc = self.doc(obj)
302 302 if doc is not None:
303 303 doc = doc.strip().splitlines()[0].strip()
304 304 return doc
305 305
306 306 def iter(self, obj):
307 307 """
308 308 Return an iterator for this attribute of the object ``obj``.
309 309 """
310 310 return xiter(self.value(obj))
311 311
312 312
313 313 class SelfDescriptor(Descriptor):
314 314 """
315 315 A ``SelfDescriptor`` describes the object itself.
316 316 """
317 317 def key(self):
318 318 return None
319 319
320 320 def attrtype(self, obj):
321 321 return "self"
322 322
323 323 def valuetype(self, obj):
324 324 return type(obj)
325 325
326 326 def value(self, obj):
327 327 return obj
328 328
329 329 def __repr__(self):
330 330 return "Self"
331 331
332 332 selfdescriptor = SelfDescriptor() # there's no need for more than one
333 333
334 334
335 335 class AttributeDescriptor(Descriptor):
336 336 """
337 337 An ``AttributeDescriptor`` describes a simple attribute of an object.
338 338 """
339 339 __slots__ = ("_name", "_doc")
340 340
341 341 def __init__(self, name, doc=None):
342 342 self._name = name
343 343 self._doc = doc
344 344
345 345 def key(self):
346 346 return self._name
347 347
348 348 def doc(self, obj):
349 349 return self._doc
350 350
351 351 def attrtype(self, obj):
352 352 return "attr"
353 353
354 354 def valuetype(self, obj):
355 355 return type(getattr(obj, self._name))
356 356
357 357 def value(self, obj):
358 358 return getattr(obj, self._name)
359 359
360 360 def __repr__(self):
361 361 if self._doc is None:
362 362 return "Attribute(%r)" % self._name
363 363 else:
364 364 return "Attribute(%r, %r)" % (self._name, self._doc)
365 365
366 366
367 367 class IndexDescriptor(Descriptor):
368 368 """
369 369 An ``IndexDescriptor`` describes an "attribute" of an object that is fetched
370 370 via ``__getitem__``.
371 371 """
372 372 __slots__ = ("_index",)
373 373
374 374 def __init__(self, index):
375 375 self._index = index
376 376
377 377 def key(self):
378 378 return self._index
379 379
380 380 def attrtype(self, obj):
381 381 return "item"
382 382
383 383 def valuetype(self, obj):
384 384 return type(obj[self._index])
385 385
386 386 def value(self, obj):
387 387 return obj[self._index]
388 388
389 389 def __repr__(self):
390 390 return "Index(%r)" % self._index
391 391
392 392
393 393 class MethodDescriptor(Descriptor):
394 394 """
395 395 A ``MethodDescriptor`` describes a method of an object that can be called
396 396 without argument. Note that this method shouldn't change the object.
397 397 """
398 398 __slots__ = ("_name", "_doc")
399 399
400 400 def __init__(self, name, doc=None):
401 401 self._name = name
402 402 self._doc = doc
403 403
404 404 def key(self):
405 405 return self._name
406 406
407 407 def doc(self, obj):
408 408 if self._doc is None:
409 409 return getattr(obj, self._name).__doc__
410 410 return self._doc
411 411
412 412 def attrtype(self, obj):
413 413 return "method"
414 414
415 415 def valuetype(self, obj):
416 416 return type(self.value(obj))
417 417
418 418 def value(self, obj):
419 419 return getattr(obj, self._name)()
420 420
421 421 def __repr__(self):
422 422 if self._doc is None:
423 423 return "Method(%r)" % self._name
424 424 else:
425 425 return "Method(%r, %r)" % (self._name, self._doc)
426 426
427 427
428 428 class IterAttributeDescriptor(Descriptor):
429 429 """
430 430 An ``IterAttributeDescriptor`` works like an ``AttributeDescriptor`` but
431 431 doesn't return an attribute values (because this value might be e.g. a large
432 432 list).
433 433 """
434 434 __slots__ = ("_name", "_doc")
435 435
436 436 def __init__(self, name, doc=None):
437 437 self._name = name
438 438 self._doc = doc
439 439
440 440 def key(self):
441 441 return self._name
442 442
443 443 def doc(self, obj):
444 444 return self._doc
445 445
446 446 def attrtype(self, obj):
447 447 return "iter"
448 448
449 449 def valuetype(self, obj):
450 450 return noitem
451 451
452 452 def value(self, obj):
453 453 return noitem
454 454
455 455 def iter(self, obj):
456 456 return xiter(getattr(obj, self._name))
457 457
458 458 def __repr__(self):
459 459 if self._doc is None:
460 460 return "IterAttribute(%r)" % self._name
461 461 else:
462 462 return "IterAttribute(%r, %r)" % (self._name, self._doc)
463 463
464 464
465 465 class IterMethodDescriptor(Descriptor):
466 466 """
467 467 An ``IterMethodDescriptor`` works like an ``MethodDescriptor`` but doesn't
468 468 return an attribute values (because this value might be e.g. a large list).
469 469 """
470 470 __slots__ = ("_name", "_doc")
471 471
472 472 def __init__(self, name, doc=None):
473 473 self._name = name
474 474 self._doc = doc
475 475
476 476 def key(self):
477 477 return self._name
478 478
479 479 def doc(self, obj):
480 480 if self._doc is None:
481 481 return getattr(obj, self._name).__doc__
482 482 return self._doc
483 483
484 484 def attrtype(self, obj):
485 485 return "itermethod"
486 486
487 487 def valuetype(self, obj):
488 488 return noitem
489 489
490 490 def value(self, obj):
491 491 return noitem
492 492
493 493 def iter(self, obj):
494 494 return xiter(getattr(obj, self._name)())
495 495
496 496 def __repr__(self):
497 497 if self._doc is None:
498 498 return "IterMethod(%r)" % self._name
499 499 else:
500 500 return "IterMethod(%r, %r)" % (self._name, self._doc)
501 501
502 502
503 503 class FunctionDescriptor(Descriptor):
504 504 """
505 505 A ``FunctionDescriptor`` turns a function into a descriptor. The function
506 506 will be called with the object to get the type and value of the attribute.
507 507 """
508 508 __slots__ = ("_function", "_name", "_doc")
509 509
510 510 def __init__(self, function, name=None, doc=None):
511 511 self._function = function
512 512 self._name = name
513 513 self._doc = doc
514 514
515 515 def key(self):
516 516 return self._function
517 517
518 518 def name(self):
519 519 if self._name is not None:
520 520 return self._name
521 521 return getattr(self._function, "__xname__", self._function.__name__)
522 522
523 523 def doc(self, obj):
524 524 if self._doc is None:
525 525 return self._function.__doc__
526 526 return self._doc
527 527
528 528 def attrtype(self, obj):
529 529 return "function"
530 530
531 531 def valuetype(self, obj):
532 532 return type(self._function(obj))
533 533
534 534 def value(self, obj):
535 535 return self._function(obj)
536 536
537 537 def __repr__(self):
538 538 if self._doc is None:
539 539 return "Function(%r)" % self._name
540 540 else:
541 541 return "Function(%r, %r)" % (self._name, self._doc)
542 542
543 543
544 544 class Table(object):
545 545 """
546 546 A ``Table`` is an object that produces items (just like a normal Python
547 547 iterator/generator does) and can be used as the first object in a pipeline
548 548 expression. The displayhook will open the default browser for such an object
549 549 (instead of simply printing the ``repr()`` result).
550 550 """
551 551
552 552 # We want to support ``foo`` and ``foo()`` in pipeline expression:
553 553 # So we implement the required operators (``|`` and ``+``) in the metaclass,
554 554 # instantiate the class and forward the operator to the instance
555 555 class __metaclass__(type):
556 556 def __iter__(self):
557 557 return iter(self())
558 558
559 559 def __or__(self, other):
560 560 return self() | other
561 561
562 562 def __add__(self, other):
563 563 return self() + other
564 564
565 565 def __radd__(self, other):
566 566 return other + self()
567 567
568 568 def __getitem__(self, index):
569 569 return self()[index]
570 570
571 571 def __getitem__(self, index):
572 572 return item(self, index)
573 573
574 574 def __contains__(self, item):
575 575 for haveitem in self:
576 576 if item == haveitem:
577 577 return True
578 578 return False
579 579
580 580 def __or__(self, other):
581 581 # autoinstantiate right hand side
582 582 if isinstance(other, type) and issubclass(other, (Table, Display)):
583 583 other = other()
584 584 # treat simple strings and functions as ``ieval`` instances
585 585 elif not isinstance(other, Display) and not isinstance(other, Table):
586 586 other = ieval(other)
587 587 # forward operations to the right hand side
588 588 return other.__ror__(self)
589 589
590 590 def __add__(self, other):
591 591 # autoinstantiate right hand side
592 592 if isinstance(other, type) and issubclass(other, Table):
593 593 other = other()
594 594 return ichain(self, other)
595 595
596 596 def __radd__(self, other):
597 597 # autoinstantiate left hand side
598 598 if isinstance(other, type) and issubclass(other, Table):
599 599 other = other()
600 600 return ichain(other, self)
601 601
602 602
603 603 class Pipe(Table):
604 604 """
605 605 A ``Pipe`` is an object that can be used in a pipeline expression. It
606 606 processes the objects it gets from its input ``Table``/``Pipe``. Note that
607 607 a ``Pipe`` object can't be used as the first object in a pipeline
608 608 expression, as it doesn't produces items itself.
609 609 """
610 610 class __metaclass__(Table.__metaclass__):
611 611 def __ror__(self, input):
612 612 return input | self()
613 613
614 614 def __ror__(self, input):
615 615 # autoinstantiate left hand side
616 616 if isinstance(input, type) and issubclass(input, Table):
617 617 input = input()
618 618 self.input = input
619 619 return self
620 620
621 621
622 622 def xrepr(item, mode="default"):
623 623 """
624 624 Generic function that adds color output and different display modes to ``repr``.
625 625
626 626 The result of an ``xrepr`` call is iterable and consists of ``(style, string)``
627 627 tuples. The ``style`` in this tuple must be a ``Style`` object from the
628 628 ``astring`` module. To reconfigure the output the first yielded tuple can be
629 629 a ``(aligment, full)`` tuple instead of a ``(style, string)`` tuple.
630 630 ``alignment`` can be -1 for left aligned, 0 for centered and 1 for right
631 631 aligned (the default is left alignment). ``full`` is a boolean that specifies
632 632 whether the complete output must be displayed or the ``Display`` object is
633 633 allowed to stop output after enough text has been produced (e.g. a syntax
634 634 highlighted text line would use ``True``, but for a large data structure
635 635 (i.e. a nested list, tuple or dictionary) ``False`` would be used).
636 636 The default is full output.
637 637
638 638 There are four different possible values for ``mode`` depending on where
639 639 the ``Display`` object will display ``item``:
640 640
641 641 * ``"header"``: ``item`` will be displayed in a header line (this is used by
642 642 ``ibrowse``).
643 643 * ``"footer"``: ``item`` will be displayed in a footer line (this is used by
644 644 ``ibrowse``).
645 645 * ``"cell"``: ``item`` will be displayed in a table cell/list.
646 646 * ``"default"``: default mode. If an ``xrepr`` implementation recursively
647 647 outputs objects, ``"default"`` must be passed in the recursive calls to
648 648 ``xrepr``.
649 649
650 650 If no implementation is registered for ``item``, ``xrepr`` will try the
651 651 ``__xrepr__`` method on ``item``. If ``item`` doesn't have an ``__xrepr__``
652 652 method it falls back to ``repr``/``__repr__`` for all modes.
653 653 """
654 654 try:
655 655 func = item.__xrepr__
656 656 except AttributeError:
657 657 yield (astyle.style_default, repr(item))
658 658 else:
659 659 try:
660 660 for x in func(mode):
661 661 yield x
662 662 except (KeyboardInterrupt, SystemExit):
663 663 raise
664 664 except Exception:
665 665 yield (astyle.style_default, repr(item))
666 666 xrepr = simplegeneric.generic(xrepr)
667 667
668 668
669 669 def xrepr_none(self, mode="default"):
670 670 yield (astyle.style_type_none, repr(self))
671 671 xrepr.when_object(None)(xrepr_none)
672 672
673 673
674 674 def xrepr_noitem(self, mode="default"):
675 675 yield (2, True)
676 676 yield (astyle.style_nodata, "<?>")
677 677 xrepr.when_object(noitem)(xrepr_noitem)
678 678
679 679
680 680 def xrepr_bool(self, mode="default"):
681 681 yield (astyle.style_type_bool, repr(self))
682 682 xrepr.when_type(bool)(xrepr_bool)
683 683
684 684
685 685 def xrepr_str(self, mode="default"):
686 686 if mode == "cell":
687 687 yield (astyle.style_default, repr(self.expandtabs(tab))[1:-1])
688 688 else:
689 689 yield (astyle.style_default, repr(self))
690 690 xrepr.when_type(str)(xrepr_str)
691 691
692 692
693 693 def xrepr_unicode(self, mode="default"):
694 694 if mode == "cell":
695 695 yield (astyle.style_default, repr(self.expandtabs(tab))[2:-1])
696 696 else:
697 697 yield (astyle.style_default, repr(self))
698 698 xrepr.when_type(unicode)(xrepr_unicode)
699 699
700 700
701 701 def xrepr_number(self, mode="default"):
702 702 yield (1, True)
703 703 yield (astyle.style_type_number, repr(self))
704 704 xrepr.when_type(int)(xrepr_number)
705 705 xrepr.when_type(long)(xrepr_number)
706 706 xrepr.when_type(float)(xrepr_number)
707 707
708 708
709 709 def xrepr_complex(self, mode="default"):
710 710 yield (astyle.style_type_number, repr(self))
711 711 xrepr.when_type(complex)(xrepr_number)
712 712
713 713
714 714 def xrepr_datetime(self, mode="default"):
715 715 if mode == "cell":
716 716 # Don't use strftime() here, as this requires year >= 1900
717 717 yield (astyle.style_type_datetime,
718 718 "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \
719 719 (self.year, self.month, self.day,
720 720 self.hour, self.minute, self.second,
721 721 self.microsecond),
722 722 )
723 723 else:
724 724 yield (astyle.style_type_datetime, repr(self))
725 725 xrepr.when_type(datetime.datetime)(xrepr_datetime)
726 726
727 727
728 728 def xrepr_date(self, mode="default"):
729 729 if mode == "cell":
730 730 yield (astyle.style_type_datetime,
731 731 "%04d-%02d-%02d" % (self.year, self.month, self.day))
732 732 else:
733 733 yield (astyle.style_type_datetime, repr(self))
734 734 xrepr.when_type(datetime.date)(xrepr_date)
735 735
736 736
737 737 def xrepr_time(self, mode="default"):
738 738 if mode == "cell":
739 739 yield (astyle.style_type_datetime,
740 740 "%02d:%02d:%02d.%06d" % \
741 741 (self.hour, self.minute, self.second, self.microsecond))
742 742 else:
743 743 yield (astyle.style_type_datetime, repr(self))
744 744 xrepr.when_type(datetime.time)(xrepr_time)
745 745
746 746
747 747 def xrepr_timedelta(self, mode="default"):
748 748 yield (astyle.style_type_datetime, repr(self))
749 749 xrepr.when_type(datetime.timedelta)(xrepr_timedelta)
750 750
751 751
752 752 def xrepr_type(self, mode="default"):
753 753 if self.__module__ == "__builtin__":
754 754 yield (astyle.style_type_type, self.__name__)
755 755 else:
756 756 yield (astyle.style_type_type, "%s.%s" % (self.__module__, self.__name__))
757 757 xrepr.when_type(type)(xrepr_type)
758 758
759 759
760 760 def xrepr_exception(self, mode="default"):
761 761 if self.__class__.__module__ == "exceptions":
762 762 classname = self.__class__.__name__
763 763 else:
764 764 classname = "%s.%s" % \
765 765 (self.__class__.__module__, self.__class__.__name__)
766 766 if mode == "header" or mode == "footer":
767 767 yield (astyle.style_error, "%s: %s" % (classname, self))
768 768 else:
769 769 yield (astyle.style_error, classname)
770 770 xrepr.when_type(Exception)(xrepr_exception)
771 771
772 772
773 773 def xrepr_listtuple(self, mode="default"):
774 774 if mode == "header" or mode == "footer":
775 775 if self.__class__.__module__ == "__builtin__":
776 776 classname = self.__class__.__name__
777 777 else:
778 778 classname = "%s.%s" % \
779 779 (self.__class__.__module__,self.__class__.__name__)
780 780 yield (astyle.style_default,
781 781 "<%s object with %d items at 0x%x>" % \
782 782 (classname, len(self), id(self)))
783 783 else:
784 784 yield (-1, False)
785 785 if isinstance(self, list):
786 786 yield (astyle.style_default, "[")
787 787 end = "]"
788 788 else:
789 789 yield (astyle.style_default, "(")
790 790 end = ")"
791 791 for (i, subself) in enumerate(self):
792 792 if i:
793 793 yield (astyle.style_default, ", ")
794 794 for part in xrepr(subself, "default"):
795 795 yield part
796 796 yield (astyle.style_default, end)
797 797 xrepr.when_type(list)(xrepr_listtuple)
798 798 xrepr.when_type(tuple)(xrepr_listtuple)
799 799
800 800
801 801 def xrepr_dict(self, mode="default"):
802 802 if mode == "header" or mode == "footer":
803 803 if self.__class__.__module__ == "__builtin__":
804 804 classname = self.__class__.__name__
805 805 else:
806 806 classname = "%s.%s" % \
807 807 (self.__class__.__module__,self.__class__.__name__)
808 808 yield (astyle.style_default,
809 809 "<%s object with %d items at 0x%x>" % \
810 810 (classname, len(self), id(self)))
811 811 else:
812 812 yield (-1, False)
813 813 if isinstance(self, dict):
814 814 yield (astyle.style_default, "{")
815 815 end = "}"
816 816 else:
817 817 yield (astyle.style_default, "dictproxy((")
818 818 end = "})"
819 819 for (i, (key, value)) in enumerate(self.iteritems()):
820 820 if i:
821 821 yield (astyle.style_default, ", ")
822 822 for part in xrepr(key, "default"):
823 823 yield part
824 824 yield (astyle.style_default, ": ")
825 825 for part in xrepr(value, "default"):
826 826 yield part
827 827 yield (astyle.style_default, end)
828 828 xrepr.when_type(dict)(xrepr_dict)
829 829 xrepr.when_type(types.DictProxyType)(xrepr_dict)
830 830
831 831
832 832 def upgradexattr(attr):
833 833 """
834 834 Convert an attribute descriptor string to a real descriptor object.
835 835
836 836 If attr already is a descriptor object return if unmodified. A
837 837 ``SelfDescriptor`` will be returned if ``attr`` is ``None``. ``"foo"``
838 838 returns an ``AttributeDescriptor`` for the attribute named ``"foo"``.
839 839 ``"foo()"`` returns a ``MethodDescriptor`` for the method named ``"foo"``.
840 840 ``"-foo"`` will return an ``IterAttributeDescriptor`` for the attribute
841 841 named ``"foo"`` and ``"-foo()"`` will return an ``IterMethodDescriptor``
842 842 for the method named ``"foo"``. Furthermore integer will return the appropriate
843 843 ``IndexDescriptor`` and callables will return a ``FunctionDescriptor``.
844 844 """
845 845 if attr is None:
846 846 return selfdescriptor
847 847 elif isinstance(attr, Descriptor):
848 848 return attr
849 849 elif isinstance(attr, str):
850 850 if attr.endswith("()"):
851 851 if attr.startswith("-"):
852 852 return IterMethodDescriptor(attr[1:-2])
853 853 else:
854 854 return MethodDescriptor(attr[:-2])
855 855 else:
856 856 if attr.startswith("-"):
857 857 return IterAttributeDescriptor(attr[1:])
858 858 else:
859 859 return AttributeDescriptor(attr)
860 860 elif isinstance(attr, (int, long)):
861 861 return IndexDescriptor(attr)
862 862 elif callable(attr):
863 863 return FunctionDescriptor(attr)
864 864 else:
865 865 raise TypeError("can't handle descriptor %r" % attr)
866 866
867 867
868 868 def xattrs(item, mode="default"):
869 869 """
870 870 Generic function that returns an iterable of attribute descriptors
871 871 to be used for displaying the attributes ob the object ``item`` in display
872 872 mode ``mode``.
873 873
874 874 There are two possible modes:
875 875
876 876 * ``"detail"``: The ``Display`` object wants to display a detailed list
877 877 of the object attributes.
878 878 * ``"default"``: The ``Display`` object wants to display the object in a
879 879 list view.
880 880
881 881 If no implementation is registered for the object ``item`` ``xattrs`` falls
882 882 back to trying the ``__xattrs__`` method of the object. If this doesn't
883 883 exist either, ``dir(item)`` is used for ``"detail"`` mode and ``(None,)``
884 884 for ``"default"`` mode.
885 885
886 886 The implementation must yield attribute descriptor (see the class
887 887 ``Descriptor`` for more info). The ``__xattrs__`` method may also return
888 888 attribute descriptor string (and ``None``) which will be converted to real
889 889 descriptors by ``upgradexattr()``.
890 890 """
891 891 try:
892 892 func = item.__xattrs__
893 893 except AttributeError:
894 894 if mode == "detail":
895 895 for attrname in dir(item):
896 896 yield AttributeDescriptor(attrname)
897 897 else:
898 898 yield selfdescriptor
899 899 else:
900 900 for attr in func(mode):
901 901 yield upgradexattr(attr)
902 902 xattrs = simplegeneric.generic(xattrs)
903 903
904 904
905 905 def xattrs_complex(self, mode="default"):
906 906 if mode == "detail":
907 907 return (AttributeDescriptor("real"), AttributeDescriptor("imag"))
908 908 return (selfdescriptor,)
909 909 xattrs.when_type(complex)(xattrs_complex)
910 910
911 911
912 912 def _isdict(item):
913 913 try:
914 914 itermeth = item.__class__.__iter__
915 915 except (AttributeError, TypeError):
916 916 return False
917 917 return itermeth is dict.__iter__ or itermeth is types.DictProxyType.__iter__
918 918
919 919
920 920 def _isstr(item):
921 921 if not isinstance(item, basestring):
922 922 return False
923 923 try:
924 924 itermeth = item.__class__.__iter__
925 925 except AttributeError:
926 926 return True
927 927 return False # ``__iter__`` has been redefined
928 928
929 929
930 930 def xiter(item):
931 931 """
932 932 Generic function that implements iteration for pipeline expression. If no
933 933 implementation is registered for ``item`` ``xiter`` falls back to ``iter``.
934 934 """
935 935 try:
936 936 func = item.__xiter__
937 937 except AttributeError:
938 938 if _isdict(item):
939 939 def items(item):
940 940 fields = ("key", "value")
941 941 for (key, value) in item.iteritems():
942 942 yield Fields(fields, key=key, value=value)
943 943 return items(item)
944 944 elif isinstance(item, new.module):
945 945 def items(item):
946 946 fields = ("key", "value")
947 947 for key in sorted(item.__dict__):
948 948 yield Fields(fields, key=key, value=getattr(item, key))
949 949 return items(item)
950 950 elif _isstr(item):
951 951 if not item:
952 952 raise ValueError("can't enter empty string")
953 953 lines = item.splitlines()
954 954 if len(lines) == 1:
955 955 def iterone(item):
956 956 yield item
957 957 return iterone(item)
958 958 else:
959 959 return iter(lines)
960 960 return iter(item)
961 961 else:
962 962 return iter(func()) # iter() just to be safe
963 963 xiter = simplegeneric.generic(xiter)
964 964
965 965
966 966 class ichain(Pipe):
967 967 """
968 968 Chains multiple ``Table``s into one.
969 969 """
970 970
971 971 def __init__(self, *iters):
972 972 self.iters = iters
973 973
974 974 def __iter__(self):
975 975 return itertools.chain(*self.iters)
976 976
977 977 def __xrepr__(self, mode="default"):
978 978 if mode == "header" or mode == "footer":
979 979 for (i, item) in enumerate(self.iters):
980 980 if i:
981 981 yield (astyle.style_default, "+")
982 982 if isinstance(item, Pipe):
983 983 yield (astyle.style_default, "(")
984 984 for part in xrepr(item, mode):
985 985 yield part
986 986 if isinstance(item, Pipe):
987 987 yield (astyle.style_default, ")")
988 988 else:
989 989 yield (astyle.style_default, repr(self))
990 990
991 991 def __repr__(self):
992 992 args = ", ".join([repr(it) for it in self.iters])
993 993 return "%s.%s(%s)" % \
994 994 (self.__class__.__module__, self.__class__.__name__, args)
995 995
996 996
997 997 class ifile(path.path):
998 998 """
999 999 file (or directory) object.
1000 1000 """
1001 1001
1002 1002 def getmode(self):
1003 1003 return self.stat().st_mode
1004 1004 mode = property(getmode, None, None, "Access mode")
1005 1005
1006 1006 def gettype(self):
1007 1007 data = [
1008 1008 (stat.S_ISREG, "file"),
1009 1009 (stat.S_ISDIR, "dir"),
1010 1010 (stat.S_ISCHR, "chardev"),
1011 1011 (stat.S_ISBLK, "blockdev"),
1012 1012 (stat.S_ISFIFO, "fifo"),
1013 1013 (stat.S_ISLNK, "symlink"),
1014 1014 (stat.S_ISSOCK,"socket"),
1015 1015 ]
1016 1016 lstat = self.lstat()
1017 1017 if lstat is not None:
1018 1018 types = set([text for (func, text) in data if func(lstat.st_mode)])
1019 1019 else:
1020 1020 types = set()
1021 1021 m = self.mode
1022 1022 types.update([text for (func, text) in data if func(m)])
1023 1023 return ", ".join(types)
1024 1024 type = property(gettype, None, None, "file type (file, directory, link, etc.)")
1025 1025
1026 1026 def getmodestr(self):
1027 1027 m = self.mode
1028 1028 data = [
1029 1029 (stat.S_IRUSR, "-r"),
1030 1030 (stat.S_IWUSR, "-w"),
1031 1031 (stat.S_IXUSR, "-x"),
1032 1032 (stat.S_IRGRP, "-r"),
1033 1033 (stat.S_IWGRP, "-w"),
1034 1034 (stat.S_IXGRP, "-x"),
1035 1035 (stat.S_IROTH, "-r"),
1036 1036 (stat.S_IWOTH, "-w"),
1037 1037 (stat.S_IXOTH, "-x"),
1038 1038 ]
1039 1039 return "".join([text[bool(m&bit)] for (bit, text) in data])
1040 1040
1041 1041 modestr = property(getmodestr, None, None, "Access mode as string")
1042 1042
1043 1043 def getblocks(self):
1044 1044 return self.stat().st_blocks
1045 1045 blocks = property(getblocks, None, None, "File size in blocks")
1046 1046
1047 1047 def getblksize(self):
1048 1048 return self.stat().st_blksize
1049 1049 blksize = property(getblksize, None, None, "Filesystem block size")
1050 1050
1051 1051 def getdev(self):
1052 1052 return self.stat().st_dev
1053 1053 dev = property(getdev)
1054 1054
1055 1055 def getnlink(self):
1056 1056 return self.stat().st_nlink
1057 1057 nlink = property(getnlink, None, None, "Number of links")
1058 1058
1059 1059 def getuid(self):
1060 1060 return self.stat().st_uid
1061 1061 uid = property(getuid, None, None, "User id of file owner")
1062 1062
1063 1063 def getgid(self):
1064 1064 return self.stat().st_gid
1065 1065 gid = property(getgid, None, None, "Group id of file owner")
1066 1066
1067 1067 def getowner(self):
1068 1068 stat = self.stat()
1069 1069 try:
1070 1070 return pwd.getpwuid(stat.st_uid).pw_name
1071 1071 except KeyError:
1072 1072 return stat.st_uid
1073 1073 owner = property(getowner, None, None, "Owner name (or id)")
1074 1074
1075 1075 def getgroup(self):
1076 1076 stat = self.stat()
1077 1077 try:
1078 1078 return grp.getgrgid(stat.st_gid).gr_name
1079 1079 except KeyError:
1080 1080 return stat.st_gid
1081 1081 group = property(getgroup, None, None, "Group name (or id)")
1082 1082
1083 1083 def getadate(self):
1084 1084 return datetime.datetime.utcfromtimestamp(self.atime)
1085 1085 adate = property(getadate, None, None, "Access date")
1086 1086
1087 1087 def getcdate(self):
1088 1088 return datetime.datetime.utcfromtimestamp(self.ctime)
1089 1089 cdate = property(getcdate, None, None, "Creation date")
1090 1090
1091 1091 def getmdate(self):
1092 1092 return datetime.datetime.utcfromtimestamp(self.mtime)
1093 1093 mdate = property(getmdate, None, None, "Modification date")
1094 1094
1095 1095 def mimetype(self):
1096 1096 """
1097 1097 Return MIME type guessed from the extension.
1098 1098 """
1099 1099 return mimetypes.guess_type(self.basename())[0]
1100 1100
1101 1101 def encoding(self):
1102 1102 """
1103 1103 Return guessed compression (like "compress" or "gzip").
1104 1104 """
1105 1105 return mimetypes.guess_type(self.basename())[1]
1106 1106
1107 1107 def __repr__(self):
1108 1108 return "ifile(%s)" % path._base.__repr__(self)
1109 1109
1110 1110 if sys.platform == "win32":
1111 1111 defaultattrs = (None, "type", "size", "modestr", "mdate")
1112 1112 else:
1113 1113 defaultattrs = (None, "type", "size", "modestr", "owner", "group", "mdate")
1114 1114
1115 1115 def __xattrs__(self, mode="default"):
1116 1116 if mode == "detail":
1117 1117 return (
1118 1118 "name",
1119 1119 "basename()",
1120 1120 "abspath()",
1121 1121 "realpath()",
1122 1122 "type",
1123 1123 "mode",
1124 1124 "modestr",
1125 1125 "stat()",
1126 1126 "lstat()",
1127 1127 "uid",
1128 1128 "gid",
1129 1129 "owner",
1130 1130 "group",
1131 1131 "dev",
1132 1132 "nlink",
1133 1133 "ctime",
1134 1134 "mtime",
1135 1135 "atime",
1136 1136 "cdate",
1137 1137 "mdate",
1138 1138 "adate",
1139 1139 "size",
1140 1140 "blocks",
1141 1141 "blksize",
1142 1142 "isdir()",
1143 1143 "islink()",
1144 1144 "mimetype()",
1145 1145 "encoding()",
1146 1146 "-listdir()",
1147 1147 "-dirs()",
1148 1148 "-files()",
1149 1149 "-walk()",
1150 1150 "-walkdirs()",
1151 1151 "-walkfiles()",
1152 1152 )
1153 1153 else:
1154 1154 return self.defaultattrs
1155 1155
1156 1156
1157 1157 def xiter_ifile(self):
1158 1158 if self.isdir():
1159 1159 yield (self / os.pardir).abspath()
1160 1160 for child in sorted(self.listdir()):
1161 1161 yield child
1162 1162 else:
1163 1163 f = self.open("rb")
1164 1164 for line in f:
1165 1165 yield line
1166 1166 f.close()
1167 1167 xiter.when_type(ifile)(xiter_ifile)
1168 1168
1169 1169
1170 1170 # We need to implement ``xrepr`` for ``ifile`` as a generic function, because
1171 1171 # otherwise ``xrepr_str`` would kick in.
1172 1172 def xrepr_ifile(self, mode="default"):
1173 1173 try:
1174 1174 if self.isdir():
1175 1175 name = "idir"
1176 1176 style = astyle.style_dir
1177 1177 else:
1178 1178 name = "ifile"
1179 1179 style = astyle.style_file
1180 1180 except IOError:
1181 1181 name = "ifile"
1182 1182 style = astyle.style_default
1183 1183 if mode in ("cell", "header", "footer"):
1184 1184 abspath = repr(path._base(self.normpath()))
1185 1185 if abspath.startswith("u"):
1186 1186 abspath = abspath[2:-1]
1187 1187 else:
1188 1188 abspath = abspath[1:-1]
1189 1189 if mode == "cell":
1190 1190 yield (style, abspath)
1191 1191 else:
1192 1192 yield (style, "%s(%s)" % (name, abspath))
1193 1193 else:
1194 1194 yield (style, repr(self))
1195 1195 xrepr.when_type(ifile)(xrepr_ifile)
1196 1196
1197 1197
1198 1198 class ils(Table):
1199 1199 """
1200 1200 List the current (or a specified) directory.
1201 1201
1202 1202 Examples:
1203 1203
1204 1204 >>> ils
1205 1205 >>> ils("/usr/local/lib/python2.4")
1206 1206 >>> ils("~")
1207 1207 """
1208 1208 def __init__(self, base=os.curdir, dirs=True, files=True):
1209 1209 self.base = os.path.expanduser(base)
1210 1210 self.dirs = dirs
1211 1211 self.files = files
1212 1212
1213 1213 def __iter__(self):
1214 1214 base = ifile(self.base)
1215 1215 yield (base / os.pardir).abspath()
1216 1216 for child in sorted(base.listdir()):
1217 1217 if self.dirs:
1218 1218 if self.files:
1219 1219 yield child
1220 1220 else:
1221 1221 if child.isdir():
1222 1222 yield child
1223 1223 elif self.files:
1224 1224 if not child.isdir():
1225 1225 yield child
1226 1226
1227 1227 def __xrepr__(self, mode="default"):
1228 1228 return xrepr(ifile(self.base), mode)
1229 1229
1230 1230 def __repr__(self):
1231 1231 return "%s.%s(%r)" % \
1232 1232 (self.__class__.__module__, self.__class__.__name__, self.base)
1233 1233
1234 1234
1235 1235 class iglob(Table):
1236 1236 """
1237 1237 List all files and directories matching a specified pattern.
1238 1238 (See ``glob.glob()`` for more info.).
1239 1239
1240 1240 Examples:
1241 1241
1242 1242 >>> iglob("*.py")
1243 1243 """
1244 1244 def __init__(self, glob):
1245 1245 self.glob = glob
1246 1246
1247 1247 def __iter__(self):
1248 1248 for name in glob.glob(self.glob):
1249 1249 yield ifile(name)
1250 1250
1251 1251 def __xrepr__(self, mode="default"):
1252 1252 if mode == "header" or mode == "footer" or mode == "cell":
1253 1253 yield (astyle.style_default,
1254 1254 "%s(%r)" % (self.__class__.__name__, self.glob))
1255 1255 else:
1256 1256 yield (astyle.style_default, repr(self))
1257 1257
1258 1258 def __repr__(self):
1259 1259 return "%s.%s(%r)" % \
1260 1260 (self.__class__.__module__, self.__class__.__name__, self.glob)
1261 1261
1262 1262
1263 1263 class iwalk(Table):
1264 1264 """
1265 1265 List all files and directories in a directory and it's subdirectory.
1266 1266
1267 1267 >>> iwalk
1268 1268 >>> iwalk("/usr/local/lib/python2.4")
1269 1269 >>> iwalk("~")
1270 1270 """
1271 1271 def __init__(self, base=os.curdir, dirs=True, files=True):
1272 1272 self.base = os.path.expanduser(base)
1273 1273 self.dirs = dirs
1274 1274 self.files = files
1275 1275
1276 1276 def __iter__(self):
1277 1277 for (dirpath, dirnames, filenames) in os.walk(self.base):
1278 1278 if self.dirs:
1279 1279 for name in sorted(dirnames):
1280 1280 yield ifile(os.path.join(dirpath, name))
1281 1281 if self.files:
1282 1282 for name in sorted(filenames):
1283 1283 yield ifile(os.path.join(dirpath, name))
1284 1284
1285 1285 def __xrepr__(self, mode="default"):
1286 1286 if mode == "header" or mode == "footer" or mode == "cell":
1287 1287 yield (astyle.style_default,
1288 1288 "%s(%r)" % (self.__class__.__name__, self.base))
1289 1289 else:
1290 1290 yield (astyle.style_default, repr(self))
1291 1291
1292 1292 def __repr__(self):
1293 1293 return "%s.%s(%r)" % \
1294 1294 (self.__class__.__module__, self.__class__.__name__, self.base)
1295 1295
1296 1296
1297 1297 class ipwdentry(object):
1298 1298 """
1299 1299 ``ipwdentry`` objects encapsulate entries in the Unix user account and
1300 1300 password database.
1301 1301 """
1302 1302 def __init__(self, id):
1303 1303 self._id = id
1304 1304 self._entry = None
1305 1305
1306 1306 def __eq__(self, other):
1307 1307 return self.__class__ is other.__class__ and self._id == other._id
1308 1308
1309 1309 def __ne__(self, other):
1310 1310 return self.__class__ is not other.__class__ or self._id != other._id
1311 1311
1312 1312 def _getentry(self):
1313 1313 if self._entry is None:
1314 1314 if isinstance(self._id, basestring):
1315 1315 self._entry = pwd.getpwnam(self._id)
1316 1316 else:
1317 1317 self._entry = pwd.getpwuid(self._id)
1318 1318 return self._entry
1319 1319
1320 1320 def getname(self):
1321 1321 if isinstance(self._id, basestring):
1322 1322 return self._id
1323 1323 else:
1324 1324 return self._getentry().pw_name
1325 1325 name = property(getname, None, None, "User name")
1326 1326
1327 1327 def getpasswd(self):
1328 1328 return self._getentry().pw_passwd
1329 1329 passwd = property(getpasswd, None, None, "Password")
1330 1330
1331 1331 def getuid(self):
1332 1332 if isinstance(self._id, basestring):
1333 1333 return self._getentry().pw_uid
1334 1334 else:
1335 1335 return self._id
1336 1336 uid = property(getuid, None, None, "User id")
1337 1337
1338 1338 def getgid(self):
1339 1339 return self._getentry().pw_gid
1340 1340 gid = property(getgid, None, None, "Primary group id")
1341 1341
1342 1342 def getgroup(self):
1343 1343 return igrpentry(self.gid)
1344 1344 group = property(getgroup, None, None, "Group")
1345 1345
1346 1346 def getgecos(self):
1347 1347 return self._getentry().pw_gecos
1348 1348 gecos = property(getgecos, None, None, "Information (e.g. full user name)")
1349 1349
1350 1350 def getdir(self):
1351 1351 return self._getentry().pw_dir
1352 1352 dir = property(getdir, None, None, "$HOME directory")
1353 1353
1354 1354 def getshell(self):
1355 1355 return self._getentry().pw_shell
1356 1356 shell = property(getshell, None, None, "Login shell")
1357 1357
1358 1358 def __xattrs__(self, mode="default"):
1359 1359 return ("name", "passwd", "uid", "gid", "gecos", "dir", "shell")
1360 1360
1361 1361 def __repr__(self):
1362 1362 return "%s.%s(%r)" % \
1363 1363 (self.__class__.__module__, self.__class__.__name__, self._id)
1364 1364
1365 1365
1366 1366 class ipwd(Table):
1367 1367 """
1368 1368 List all entries in the Unix user account and password database.
1369 1369
1370 1370 Example:
1371 1371
1372 1372 >>> ipwd | isort("uid")
1373 1373 """
1374 1374 def __iter__(self):
1375 1375 for entry in pwd.getpwall():
1376 1376 yield ipwdentry(entry.pw_name)
1377 1377
1378 1378 def __xrepr__(self, mode="default"):
1379 1379 if mode == "header" or mode == "footer" or mode == "cell":
1380 1380 yield (astyle.style_default, "%s()" % self.__class__.__name__)
1381 1381 else:
1382 1382 yield (astyle.style_default, repr(self))
1383 1383
1384 1384
1385 1385 class igrpentry(object):
1386 1386 """
1387 1387 ``igrpentry`` objects encapsulate entries in the Unix group database.
1388 1388 """
1389 1389 def __init__(self, id):
1390 1390 self._id = id
1391 1391 self._entry = None
1392 1392
1393 1393 def __eq__(self, other):
1394 1394 return self.__class__ is other.__class__ and self._id == other._id
1395 1395
1396 1396 def __ne__(self, other):
1397 1397 return self.__class__ is not other.__class__ or self._id != other._id
1398 1398
1399 1399 def _getentry(self):
1400 1400 if self._entry is None:
1401 1401 if isinstance(self._id, basestring):
1402 1402 self._entry = grp.getgrnam(self._id)
1403 1403 else:
1404 1404 self._entry = grp.getgrgid(self._id)
1405 1405 return self._entry
1406 1406
1407 1407 def getname(self):
1408 1408 if isinstance(self._id, basestring):
1409 1409 return self._id
1410 1410 else:
1411 1411 return self._getentry().gr_name
1412 1412 name = property(getname, None, None, "Group name")
1413 1413
1414 1414 def getpasswd(self):
1415 1415 return self._getentry().gr_passwd
1416 1416 passwd = property(getpasswd, None, None, "Password")
1417 1417
1418 1418 def getgid(self):
1419 1419 if isinstance(self._id, basestring):
1420 1420 return self._getentry().gr_gid
1421 1421 else:
1422 1422 return self._id
1423 1423 gid = property(getgid, None, None, "Group id")
1424 1424
1425 1425 def getmem(self):
1426 1426 return self._getentry().gr_mem
1427 1427 mem = property(getmem, None, None, "Members")
1428 1428
1429 1429 def __xattrs__(self, mode="default"):
1430 1430 return ("name", "passwd", "gid", "mem")
1431 1431
1432 1432 def __xrepr__(self, mode="default"):
1433 1433 if mode == "header" or mode == "footer" or mode == "cell":
1434 1434 yield (astyle.style_default, "group ")
1435 1435 try:
1436 1436 yield (astyle.style_default, self.name)
1437 1437 except KeyError:
1438 1438 if isinstance(self._id, basestring):
1439 1439 yield (astyle.style_default, self.name_id)
1440 1440 else:
1441 1441 yield (astyle.style_type_number, str(self._id))
1442 1442 else:
1443 1443 yield (astyle.style_default, repr(self))
1444 1444
1445 1445 def __iter__(self):
1446 1446 for member in self.mem:
1447 1447 yield ipwdentry(member)
1448 1448
1449 1449 def __repr__(self):
1450 1450 return "%s.%s(%r)" % \
1451 1451 (self.__class__.__module__, self.__class__.__name__, self._id)
1452 1452
1453 1453
1454 1454 class igrp(Table):
1455 1455 """
1456 1456 This ``Table`` lists all entries in the Unix group database.
1457 1457 """
1458 1458 def __iter__(self):
1459 1459 for entry in grp.getgrall():
1460 1460 yield igrpentry(entry.gr_name)
1461 1461
1462 1462 def __xrepr__(self, mode="default"):
1463 1463 if mode == "header" or mode == "footer":
1464 1464 yield (astyle.style_default, "%s()" % self.__class__.__name__)
1465 1465 else:
1466 1466 yield (astyle.style_default, repr(self))
1467 1467
1468 1468
1469 1469 class Fields(object):
1470 1470 def __init__(self, fieldnames, **fields):
1471 1471 self.__fieldnames = [upgradexattr(fieldname) for fieldname in fieldnames]
1472 1472 for (key, value) in fields.iteritems():
1473 1473 setattr(self, key, value)
1474 1474
1475 1475 def __xattrs__(self, mode="default"):
1476 1476 return self.__fieldnames
1477 1477
1478 1478 def __xrepr__(self, mode="default"):
1479 1479 yield (-1, False)
1480 1480 if mode == "header" or mode == "cell":
1481 1481 yield (astyle.style_default, self.__class__.__name__)
1482 1482 yield (astyle.style_default, "(")
1483 1483 for (i, f) in enumerate(self.__fieldnames):
1484 1484 if i:
1485 1485 yield (astyle.style_default, ", ")
1486 1486 yield (astyle.style_default, f.name())
1487 1487 yield (astyle.style_default, "=")
1488 1488 for part in xrepr(getattr(self, f), "default"):
1489 1489 yield part
1490 1490 yield (astyle.style_default, ")")
1491 1491 elif mode == "footer":
1492 1492 yield (astyle.style_default, self.__class__.__name__)
1493 1493 yield (astyle.style_default, "(")
1494 1494 for (i, f) in enumerate(self.__fieldnames):
1495 1495 if i:
1496 1496 yield (astyle.style_default, ", ")
1497 1497 yield (astyle.style_default, f.name())
1498 1498 yield (astyle.style_default, ")")
1499 1499 else:
1500 1500 yield (astyle.style_default, repr(self))
1501 1501
1502 1502
1503 1503 class FieldTable(Table, list):
1504 1504 def __init__(self, *fields):
1505 1505 Table.__init__(self)
1506 1506 list.__init__(self)
1507 1507 self.fields = fields
1508 1508
1509 1509 def add(self, **fields):
1510 1510 self.append(Fields(self.fields, **fields))
1511 1511
1512 1512 def __xrepr__(self, mode="default"):
1513 1513 yield (-1, False)
1514 1514 if mode == "header" or mode == "footer":
1515 1515 yield (astyle.style_default, self.__class__.__name__)
1516 1516 yield (astyle.style_default, "(")
1517 1517 for (i, f) in enumerate(self.__fieldnames):
1518 1518 if i:
1519 1519 yield (astyle.style_default, ", ")
1520 1520 yield (astyle.style_default, f)
1521 1521 yield (astyle.style_default, ")")
1522 1522 else:
1523 1523 yield (astyle.style_default, repr(self))
1524 1524
1525 1525 def __repr__(self):
1526 1526 return "<%s.%s object with fields=%r at 0x%x>" % \
1527 1527 (self.__class__.__module__, self.__class__.__name__,
1528 1528 ", ".join(map(repr, self.fields)), id(self))
1529 1529
1530 1530
1531 1531 class List(list):
1532 1532 def __xattrs__(self, mode="default"):
1533 1533 return xrange(len(self))
1534 1534
1535 1535 def __xrepr__(self, mode="default"):
1536 1536 yield (-1, False)
1537 1537 if mode == "header" or mode == "cell" or mode == "footer" or mode == "default":
1538 1538 yield (astyle.style_default, self.__class__.__name__)
1539 1539 yield (astyle.style_default, "(")
1540 1540 for (i, item) in enumerate(self):
1541 1541 if i:
1542 1542 yield (astyle.style_default, ", ")
1543 1543 for part in xrepr(item, "default"):
1544 1544 yield part
1545 1545 yield (astyle.style_default, ")")
1546 1546 else:
1547 1547 yield (astyle.style_default, repr(self))
1548 1548
1549 1549
1550 1550 class ienv(Table):
1551 1551 """
1552 1552 List environment variables.
1553 1553
1554 1554 Example:
1555 1555
1556 1556 >>> ienv
1557 1557 """
1558 1558
1559 1559 def __iter__(self):
1560 1560 fields = ("key", "value")
1561 1561 for (key, value) in os.environ.iteritems():
1562 1562 yield Fields(fields, key=key, value=value)
1563 1563
1564 1564 def __xrepr__(self, mode="default"):
1565 1565 if mode == "header" or mode == "cell":
1566 1566 yield (astyle.style_default, "%s()" % self.__class__.__name__)
1567 1567 else:
1568 1568 yield (astyle.style_default, repr(self))
1569 1569
1570 1570
1571 class ihist(Table):
1572 """
1573 IPython input history
1574
1575 Example:
1576
1577 >>> ihist
1578 >>> ihist(True) (raw mode)
1579 """
1580 def __init__(self, raw=False):
1581 self.raw = raw
1582
1583 def __iter__(self):
1584 api = ipapi.get()
1585 if self.raw:
1586 for line in api.IP.input_hist_raw:
1587 yield line.rstrip("\n")
1588 else:
1589 for line in api.IP.input_hist:
1590 yield line.rstrip("\n")
1591
1592
1571 1593 class icsv(Pipe):
1572 1594 """
1573 1595 This ``Pipe`` lists turn the input (with must be a pipe outputting lines
1574 1596 or an ``ifile``) into lines of CVS columns.
1575 1597 """
1576 1598 def __init__(self, **csvargs):
1577 1599 """
1578 1600 Create an ``icsv`` object. ``cvsargs`` will be passed through as
1579 1601 keyword arguments to ``cvs.reader()``.
1580 1602 """
1581 1603 self.csvargs = csvargs
1582 1604
1583 1605 def __iter__(self):
1584 1606 input = self.input
1585 1607 if isinstance(input, ifile):
1586 1608 input = input.open("rb")
1587 1609 reader = csv.reader(input, **self.csvargs)
1588 1610 for line in reader:
1589 1611 yield List(line)
1590 1612
1591 1613 def __xrepr__(self, mode="default"):
1592 1614 yield (-1, False)
1593 1615 if mode == "header" or mode == "footer":
1594 1616 input = getattr(self, "input", None)
1595 1617 if input is not None:
1596 1618 for part in xrepr(input, mode):
1597 1619 yield part
1598 1620 yield (astyle.style_default, " | ")
1599 1621 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1600 1622 for (i, (name, value)) in enumerate(self.csvargs.iteritems()):
1601 1623 if i:
1602 1624 yield (astyle.style_default, ", ")
1603 1625 yield (astyle.style_default, name)
1604 1626 yield (astyle.style_default, "=")
1605 1627 for part in xrepr(value, "default"):
1606 1628 yield part
1607 1629 yield (astyle.style_default, ")")
1608 1630 else:
1609 1631 yield (astyle.style_default, repr(self))
1610 1632
1611 1633 def __repr__(self):
1612 1634 args = ", ".join(["%s=%r" % item for item in self.csvargs.iteritems()])
1613 1635 return "<%s.%s %s at 0x%x>" % \
1614 1636 (self.__class__.__module__, self.__class__.__name__, args, id(self))
1615 1637
1616 1638
1617 1639 class ix(Table):
1618 1640 """
1619 1641 Execute a system command and list its output as lines
1620 1642 (similar to ``os.popen()``).
1621 1643
1622 1644 Examples:
1623 1645
1624 1646 >>> ix("ps x")
1625 1647 >>> ix("find .") | ifile
1626 1648 """
1627 1649 def __init__(self, cmd):
1628 1650 self.cmd = cmd
1629 1651 self._pipeout = None
1630 1652
1631 1653 def __iter__(self):
1632 1654 (_pipein, self._pipeout) = os.popen4(self.cmd)
1633 1655 _pipein.close()
1634 1656 for l in self._pipeout:
1635 1657 yield l.rstrip("\r\n")
1636 1658 self._pipeout.close()
1637 1659 self._pipeout = None
1638 1660
1639 1661 def __del__(self):
1640 1662 if self._pipeout is not None and not self._pipeout.closed:
1641 1663 self._pipeout.close()
1642 1664 self._pipeout = None
1643 1665
1644 1666 def __xrepr__(self, mode="default"):
1645 1667 if mode == "header" or mode == "footer":
1646 1668 yield (astyle.style_default,
1647 1669 "%s(%r)" % (self.__class__.__name__, self.cmd))
1648 1670 else:
1649 1671 yield (astyle.style_default, repr(self))
1650 1672
1651 1673 def __repr__(self):
1652 1674 return "%s.%s(%r)" % \
1653 1675 (self.__class__.__module__, self.__class__.__name__, self.cmd)
1654 1676
1655 1677
1656 1678 class ifilter(Pipe):
1657 1679 """
1658 1680 Filter an input pipe. Only objects where an expression evaluates to true
1659 1681 (and doesn't raise an exception) are listed.
1660 1682
1661 1683 Examples:
1662 1684
1663 1685 >>> ils | ifilter("_.isfile() and size>1000")
1664 1686 >>> igrp | ifilter("len(mem)")
1665 1687 >>> sys.modules | ifilter(lambda _:_.value is not None)
1666 1688 """
1667 1689
1668 1690 def __init__(self, expr, globals=None, errors="raiseifallfail"):
1669 1691 """
1670 1692 Create an ``ifilter`` object. ``expr`` can be a callable or a string
1671 1693 containing an expression. ``globals`` will be used as the global
1672 1694 namespace for calling string expressions (defaulting to IPython's
1673 1695 user namespace). ``errors`` specifies how exception during evaluation
1674 1696 of ``expr`` are handled:
1675 1697
1676 1698 * ``drop``: drop all items that have errors;
1677 1699
1678 1700 * ``keep``: keep all items that have errors;
1679 1701
1680 1702 * ``keeperror``: keep the exception of all items that have errors;
1681 1703
1682 1704 * ``raise``: raise the exception;
1683 1705
1684 1706 * ``raiseifallfail``: raise the first exception if all items have errors;
1685 1707 otherwise drop those with errors (this is the default).
1686 1708 """
1687 1709 self.expr = expr
1688 1710 self.globals = globals
1689 1711 self.errors = errors
1690 1712
1691 1713 def __iter__(self):
1692 1714 if callable(self.expr):
1693 1715 test = self.expr
1694 1716 else:
1695 1717 g = getglobals(self.globals)
1696 1718 expr = compile(self.expr, "ipipe-expression", "eval")
1697 1719 def test(item):
1698 1720 return eval(expr, g, AttrNamespace(item))
1699 1721
1700 1722 ok = 0
1701 1723 exc_info = None
1702 1724 for item in xiter(self.input):
1703 1725 try:
1704 1726 if test(item):
1705 1727 yield item
1706 1728 ok += 1
1707 1729 except (KeyboardInterrupt, SystemExit):
1708 1730 raise
1709 1731 except Exception, exc:
1710 1732 if self.errors == "drop":
1711 1733 pass # Ignore errors
1712 1734 elif self.errors == "keep":
1713 1735 yield item
1714 1736 elif self.errors == "keeperror":
1715 1737 yield exc
1716 1738 elif self.errors == "raise":
1717 1739 raise
1718 1740 elif self.errors == "raiseifallfail":
1719 1741 if exc_info is None:
1720 1742 exc_info = sys.exc_info()
1721 1743 if not ok and exc_info is not None:
1722 1744 raise exc_info[0], exc_info[1], exc_info[2]
1723 1745
1724 1746 def __xrepr__(self, mode="default"):
1725 1747 if mode == "header" or mode == "footer":
1726 1748 input = getattr(self, "input", None)
1727 1749 if input is not None:
1728 1750 for part in xrepr(input, mode):
1729 1751 yield part
1730 1752 yield (astyle.style_default, " | ")
1731 1753 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1732 1754 for part in xrepr(self.expr, "default"):
1733 1755 yield part
1734 1756 yield (astyle.style_default, ")")
1735 1757 else:
1736 1758 yield (astyle.style_default, repr(self))
1737 1759
1738 1760 def __repr__(self):
1739 1761 return "<%s.%s expr=%r at 0x%x>" % \
1740 1762 (self.__class__.__module__, self.__class__.__name__,
1741 1763 self.expr, id(self))
1742 1764
1743 1765
1744 1766 class ieval(Pipe):
1745 1767 """
1746 1768 Evaluate an expression for each object in the input pipe.
1747 1769
1748 1770 Examples:
1749 1771
1750 1772 >>> ils | ieval("_.abspath()")
1751 1773 >>> sys.path | ieval(ifile)
1752 1774 """
1753 1775
1754 1776 def __init__(self, expr, globals=None, errors="raiseifallfail"):
1755 1777 """
1756 1778 Create an ``ieval`` object. ``expr`` can be a callable or a string
1757 1779 containing an expression. For the meaning of ``globals`` and
1758 1780 ``errors`` see ``ifilter``.
1759 1781 """
1760 1782 self.expr = expr
1761 1783 self.globals = globals
1762 1784 self.errors = errors
1763 1785
1764 1786 def __iter__(self):
1765 1787 if callable(self.expr):
1766 1788 do = self.expr
1767 1789 else:
1768 1790 g = getglobals(self.globals)
1769 1791 expr = compile(self.expr, "ipipe-expression", "eval")
1770 1792 def do(item):
1771 1793 return eval(expr, g, AttrNamespace(item))
1772 1794
1773 1795 ok = 0
1774 1796 exc_info = None
1775 1797 for item in xiter(self.input):
1776 1798 try:
1777 1799 yield do(item)
1778 1800 except (KeyboardInterrupt, SystemExit):
1779 1801 raise
1780 1802 except Exception, exc:
1781 1803 if self.errors == "drop":
1782 1804 pass # Ignore errors
1783 1805 elif self.errors == "keep":
1784 1806 yield item
1785 1807 elif self.errors == "keeperror":
1786 1808 yield exc
1787 1809 elif self.errors == "raise":
1788 1810 raise
1789 1811 elif self.errors == "raiseifallfail":
1790 1812 if exc_info is None:
1791 1813 exc_info = sys.exc_info()
1792 1814 if not ok and exc_info is not None:
1793 1815 raise exc_info[0], exc_info[1], exc_info[2]
1794 1816
1795 1817 def __xrepr__(self, mode="default"):
1796 1818 if mode == "header" or mode == "footer":
1797 1819 input = getattr(self, "input", None)
1798 1820 if input is not None:
1799 1821 for part in xrepr(input, mode):
1800 1822 yield part
1801 1823 yield (astyle.style_default, " | ")
1802 1824 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1803 1825 for part in xrepr(self.expr, "default"):
1804 1826 yield part
1805 1827 yield (astyle.style_default, ")")
1806 1828 else:
1807 1829 yield (astyle.style_default, repr(self))
1808 1830
1809 1831 def __repr__(self):
1810 1832 return "<%s.%s expr=%r at 0x%x>" % \
1811 1833 (self.__class__.__module__, self.__class__.__name__,
1812 1834 self.expr, id(self))
1813 1835
1814 1836
1815 1837 class ienum(Pipe):
1816 1838 """
1817 1839 Enumerate the input pipe (i.e. wrap each input object in an object
1818 1840 with ``index`` and ``object`` attributes).
1819 1841
1820 1842 Examples:
1821 1843
1822 1844 >>> xrange(20) | ieval("_,_*_") | ienum | ifilter("index % 2 == 0") | ieval("object")
1823 1845 """
1824 1846 def __iter__(self):
1825 1847 fields = ("index", "object")
1826 1848 for (index, object) in enumerate(xiter(self.input)):
1827 1849 yield Fields(fields, index=index, object=object)
1828 1850
1829 1851
1830 1852 class isort(Pipe):
1831 1853 """
1832 1854 Sorts the input pipe.
1833 1855
1834 1856 Examples:
1835 1857
1836 1858 >>> ils | isort("size")
1837 1859 >>> ils | isort("_.isdir(), _.lower()", reverse=True)
1838 1860 """
1839 1861
1840 1862 def __init__(self, key=None, globals=None, reverse=False):
1841 1863 """
1842 1864 Create an ``isort`` object. ``key`` can be a callable or a string
1843 1865 containing an expression (or ``None`` in which case the items
1844 1866 themselves will be sorted). If ``reverse`` is true the sort order
1845 1867 will be reversed. For the meaning of ``globals`` see ``ifilter``.
1846 1868 """
1847 1869 self.key = key
1848 1870 self.globals = globals
1849 1871 self.reverse = reverse
1850 1872
1851 1873 def __iter__(self):
1852 1874 if self.key is None:
1853 1875 items = sorted(xiter(self.input), reverse=self.reverse)
1854 1876 elif callable(self.key):
1855 1877 items = sorted(xiter(self.input), key=self.key, reverse=self.reverse)
1856 1878 else:
1857 1879 g = getglobals(self.globals)
1858 1880 key = compile(self.key, "ipipe-expression", "eval")
1859 1881 def realkey(item):
1860 1882 return eval(key, g, AttrNamespace(item))
1861 1883 items = sorted(xiter(self.input), key=realkey, reverse=self.reverse)
1862 1884 for item in items:
1863 1885 yield item
1864 1886
1865 1887 def __xrepr__(self, mode="default"):
1866 1888 if mode == "header" or mode == "footer":
1867 1889 input = getattr(self, "input", None)
1868 1890 if input is not None:
1869 1891 for part in xrepr(input, mode):
1870 1892 yield part
1871 1893 yield (astyle.style_default, " | ")
1872 1894 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1873 1895 for part in xrepr(self.key, "default"):
1874 1896 yield part
1875 1897 if self.reverse:
1876 1898 yield (astyle.style_default, ", ")
1877 1899 for part in xrepr(True, "default"):
1878 1900 yield part
1879 1901 yield (astyle.style_default, ")")
1880 1902 else:
1881 1903 yield (astyle.style_default, repr(self))
1882 1904
1883 1905 def __repr__(self):
1884 1906 return "<%s.%s key=%r reverse=%r at 0x%x>" % \
1885 1907 (self.__class__.__module__, self.__class__.__name__,
1886 1908 self.key, self.reverse, id(self))
1887 1909
1888 1910
1889 1911 tab = 3 # for expandtabs()
1890 1912
1891 1913 def _format(field):
1892 1914 if isinstance(field, str):
1893 1915 text = repr(field.expandtabs(tab))[1:-1]
1894 1916 elif isinstance(field, unicode):
1895 1917 text = repr(field.expandtabs(tab))[2:-1]
1896 1918 elif isinstance(field, datetime.datetime):
1897 1919 # Don't use strftime() here, as this requires year >= 1900
1898 1920 text = "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \
1899 1921 (field.year, field.month, field.day,
1900 1922 field.hour, field.minute, field.second, field.microsecond)
1901 1923 elif isinstance(field, datetime.date):
1902 1924 text = "%04d-%02d-%02d" % (field.year, field.month, field.day)
1903 1925 else:
1904 1926 text = repr(field)
1905 1927 return text
1906 1928
1907 1929
1908 1930 class Display(object):
1909 1931 class __metaclass__(type):
1910 1932 def __ror__(self, input):
1911 1933 return input | self()
1912 1934
1913 1935 def __ror__(self, input):
1914 1936 self.input = input
1915 1937 return self
1916 1938
1917 1939 def display(self):
1918 1940 pass
1919 1941
1920 1942
1921 1943 class iless(Display):
1922 1944 cmd = "less --quit-if-one-screen --LONG-PROMPT --LINE-NUMBERS --chop-long-lines --shift=8 --RAW-CONTROL-CHARS"
1923 1945
1924 1946 def display(self):
1925 1947 try:
1926 1948 pager = os.popen(self.cmd, "w")
1927 1949 try:
1928 1950 for item in xiter(self.input):
1929 1951 first = False
1930 1952 for attr in xattrs(item, "default"):
1931 1953 if first:
1932 1954 first = False
1933 1955 else:
1934 1956 pager.write(" ")
1935 1957 attr = upgradexattr(attr)
1936 1958 if not isinstance(attr, SelfDescriptor):
1937 1959 pager.write(attr.name())
1938 1960 pager.write("=")
1939 1961 pager.write(str(attr.value(item)))
1940 1962 pager.write("\n")
1941 1963 finally:
1942 1964 pager.close()
1943 1965 except Exception, exc:
1944 1966 print "%s: %s" % (exc.__class__.__name__, str(exc))
1945 1967
1946 1968
1947 1969 def xformat(value, mode, maxlength):
1948 1970 align = None
1949 1971 full = True
1950 1972 width = 0
1951 1973 text = astyle.Text()
1952 1974 for (style, part) in xrepr(value, mode):
1953 1975 # only consider the first result
1954 1976 if align is None:
1955 1977 if isinstance(style, int):
1956 1978 # (style, text) really is (alignment, stop)
1957 1979 align = style
1958 1980 full = part
1959 1981 continue
1960 1982 else:
1961 1983 align = -1
1962 1984 full = True
1963 1985 if not isinstance(style, int):
1964 1986 text.append((style, part))
1965 1987 width += len(part)
1966 1988 if width >= maxlength and not full:
1967 1989 text.append((astyle.style_ellisis, "..."))
1968 1990 width += 3
1969 1991 break
1970 1992 if align is None: # default to left alignment
1971 1993 align = -1
1972 1994 return (align, width, text)
1973 1995
1974 1996
1975 1997 class idump(Display):
1976 1998 # The approximate maximum length of a column entry
1977 1999 maxattrlength = 200
1978 2000
1979 2001 # Style for column names
1980 2002 style_header = astyle.Style.fromstr("white:black:bold")
1981 2003
1982 2004 def __init__(self, *attrs):
1983 2005 self.attrs = [upgradexattr(attr) for attr in attrs]
1984 2006 self.headerpadchar = " "
1985 2007 self.headersepchar = "|"
1986 2008 self.datapadchar = " "
1987 2009 self.datasepchar = "|"
1988 2010
1989 2011 def display(self):
1990 2012 stream = genutils.Term.cout
1991 2013 allattrs = []
1992 2014 attrset = set()
1993 2015 colwidths = {}
1994 2016 rows = []
1995 2017 for item in xiter(self.input):
1996 2018 row = {}
1997 2019 attrs = self.attrs
1998 2020 if not attrs:
1999 2021 attrs = xattrs(item, "default")
2000 2022 for attr in attrs:
2001 2023 if attr not in attrset:
2002 2024 allattrs.append(attr)
2003 2025 attrset.add(attr)
2004 2026 colwidths[attr] = len(attr.name())
2005 2027 try:
2006 2028 value = attr.value(item)
2007 2029 except (KeyboardInterrupt, SystemExit):
2008 2030 raise
2009 2031 except Exception, exc:
2010 2032 value = exc
2011 2033 (align, width, text) = xformat(value, "cell", self.maxattrlength)
2012 2034 colwidths[attr] = max(colwidths[attr], width)
2013 2035 # remember alignment, length and colored parts
2014 2036 row[attr] = (align, width, text)
2015 2037 rows.append(row)
2016 2038
2017 2039 stream.write("\n")
2018 2040 for (i, attr) in enumerate(allattrs):
2019 2041 attrname = attr.name()
2020 2042 self.style_header(attrname).write(stream)
2021 2043 spc = colwidths[attr] - len(attrname)
2022 2044 if i < len(colwidths)-1:
2023 2045 stream.write(self.headerpadchar*spc)
2024 2046 stream.write(self.headersepchar)
2025 2047 stream.write("\n")
2026 2048
2027 2049 for row in rows:
2028 2050 for (i, attr) in enumerate(allattrs):
2029 2051 (align, width, text) = row[attr]
2030 2052 spc = colwidths[attr] - width
2031 2053 if align == -1:
2032 2054 text.write(stream)
2033 2055 if i < len(colwidths)-1:
2034 2056 stream.write(self.datapadchar*spc)
2035 2057 elif align == 0:
2036 2058 spc = colwidths[attr] - width
2037 2059 spc1 = spc//2
2038 2060 spc2 = spc-spc1
2039 2061 stream.write(self.datapadchar*spc1)
2040 2062 text.write(stream)
2041 2063 if i < len(colwidths)-1:
2042 2064 stream.write(self.datapadchar*spc2)
2043 2065 else:
2044 2066 stream.write(self.datapadchar*spc)
2045 2067 text.write(stream)
2046 2068 if i < len(colwidths)-1:
2047 2069 stream.write(self.datasepchar)
2048 2070 stream.write("\n")
2049 2071
2050 2072
2051 2073 class AttributeDetail(Table):
2052 2074 """
2053 2075 ``AttributeDetail`` objects are use for displaying a detailed list of object
2054 2076 attributes.
2055 2077 """
2056 2078 def __init__(self, object, descriptor):
2057 2079 self.object = object
2058 2080 self.descriptor = descriptor
2059 2081
2060 2082 def __iter__(self):
2061 2083 return self.descriptor.iter(self.object)
2062 2084
2063 2085 def name(self):
2064 2086 return self.descriptor.name()
2065 2087
2066 2088 def attrtype(self):
2067 2089 return self.descriptor.attrtype(self.object)
2068 2090
2069 2091 def valuetype(self):
2070 2092 return self.descriptor.valuetype(self.object)
2071 2093
2072 2094 def doc(self):
2073 2095 return self.descriptor.doc(self.object)
2074 2096
2075 2097 def shortdoc(self):
2076 2098 return self.descriptor.shortdoc(self.object)
2077 2099
2078 2100 def value(self):
2079 2101 return self.descriptor.value(self.object)
2080 2102
2081 2103 def __xattrs__(self, mode="default"):
2082 2104 attrs = ("name()", "attrtype()", "valuetype()", "value()", "shortdoc()")
2083 2105 if mode == "detail":
2084 2106 attrs += ("doc()",)
2085 2107 return attrs
2086 2108
2087 2109 def __xrepr__(self, mode="default"):
2088 2110 yield (-1, True)
2089 2111 valuetype = self.valuetype()
2090 2112 if valuetype is not noitem:
2091 2113 for part in xrepr(valuetype):
2092 2114 yield part
2093 2115 yield (astyle.style_default, " ")
2094 2116 yield (astyle.style_default, self.attrtype())
2095 2117 yield (astyle.style_default, " ")
2096 2118 yield (astyle.style_default, self.name())
2097 2119 yield (astyle.style_default, " of ")
2098 2120 for part in xrepr(self.object):
2099 2121 yield part
2100 2122
2101 2123
2102 2124 try:
2103 2125 from ibrowse import ibrowse
2104 2126 except ImportError:
2105 2127 # No curses (probably Windows)
2106 2128 try:
2107 2129 from igrid import igrid
2108 2130 except ImportError:
2109 2131 # no wx eithevn do => use ``idump`` as the default display.
2110 2132 defaultdisplay = idump
2111 2133 else:
2112 2134 defaultdisplay = igrid
2113 2135 __all__.append("igrid")
2114 2136 else:
2115 2137 defaultdisplay = ibrowse
2116 2138 __all__.append("ibrowse")
2117 2139
2118 2140
2119 2141 # If we're running under IPython, install an IPython displayhook that
2120 2142 # returns the object from Display.display(), else install a displayhook
2121 2143 # directly as sys.displayhook
2122 2144 api = None
2123 2145 if ipapi is not None:
2124 2146 try:
2125 2147 api = ipapi.get()
2126 2148 except AttributeError:
2127 2149 pass
2128 2150
2129 2151 if api is not None:
2130 2152 def displayhook(self, obj):
2131 2153 if isinstance(obj, type) and issubclass(obj, Table):
2132 2154 obj = obj()
2133 2155 if isinstance(obj, Table):
2134 2156 obj = obj | defaultdisplay
2135 2157 if isinstance(obj, Display):
2136 2158 return obj.display()
2137 2159 else:
2138 2160 raise ipapi.TryNext
2139 2161 api.set_hook("result_display", displayhook)
2140 2162 else:
2141 2163 def installdisplayhook():
2142 2164 _originalhook = sys.displayhook
2143 2165 def displayhook(obj):
2144 2166 if isinstance(obj, type) and issubclass(obj, Table):
2145 2167 obj = obj()
2146 2168 if isinstance(obj, Table):
2147 2169 obj = obj | defaultdisplay
2148 2170 if isinstance(obj, Display):
2149 2171 return obj.display()
2150 2172 else:
2151 2173 _originalhook(obj)
2152 2174 sys.displayhook = displayhook
2153 2175 installdisplayhook()
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now