Show More
The requested changes are too big and content was truncated. Show full diff
This diff has been collapsed as it changes many lines, (764 lines changed) Show them Hide them | |||
@@ -0,0 +1,764 b'' | |||
|
1 | # -*- coding: iso-8859-1 -*- | |
|
2 | ||
|
3 | import ipipe, os, webbrowser, urllib | |
|
4 | import wx | |
|
5 | import wx.grid, wx.html | |
|
6 | ||
|
7 | try: | |
|
8 | sorted | |
|
9 | except NameError: | |
|
10 | from ipipe import sorted | |
|
11 | ||
|
12 | ||
|
13 | __all__ = ["igrid"] | |
|
14 | ||
|
15 | ||
|
16 | class IGridRenderer(wx.grid.PyGridCellRenderer): | |
|
17 | """ | |
|
18 | This is a custom renderer for our IGridGrid | |
|
19 | """ | |
|
20 | def __init__(self, table): | |
|
21 | self.maxchars = 200 | |
|
22 | self.table = table | |
|
23 | self.colormap = ( | |
|
24 | ( 0, 0, 0), | |
|
25 | (174, 0, 0), | |
|
26 | ( 0, 174, 0), | |
|
27 | (174, 174, 0), | |
|
28 | ( 0, 0, 174), | |
|
29 | (174, 0, 174), | |
|
30 | ( 0, 174, 174), | |
|
31 | ( 64, 64, 64) | |
|
32 | ) | |
|
33 | ||
|
34 | wx.grid.PyGridCellRenderer.__init__(self) | |
|
35 | ||
|
36 | def _getvalue(self, row, col): | |
|
37 | try: | |
|
38 | value = self.table.displayattrs[col].value(self.table.items[row]) | |
|
39 | (align, width, text) = ipipe.xformat(value, "cell", self.maxchars) | |
|
40 | except Exception, exc: | |
|
41 | (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars) | |
|
42 | return (align, text) | |
|
43 | ||
|
44 | def GetBestSize(self, grid, attr, dc, row, col): | |
|
45 | text = grid.GetCellValue(row, col) | |
|
46 | (align, text) = self._getvalue(row, col) | |
|
47 | dc.SetFont(attr.GetFont()) | |
|
48 | (w, h) = dc.GetTextExtent(str(text)) | |
|
49 | return wx.Size(min(w+2, 600), h+2) # add border | |
|
50 | ||
|
51 | def Draw(self, grid, attr, dc, rect, row, col, isSelected): | |
|
52 | """ | |
|
53 | Takes care of drawing everything in the cell; aligns the text | |
|
54 | """ | |
|
55 | text = grid.GetCellValue(row, col) | |
|
56 | (align, text) = self._getvalue(row, col) | |
|
57 | if isSelected: | |
|
58 | bg = grid.GetSelectionBackground() | |
|
59 | else: | |
|
60 | bg = ["white", (240, 240, 240)][row%2] | |
|
61 | dc.SetTextBackground(bg) | |
|
62 | dc.SetBrush(wx.Brush(bg, wx.SOLID)) | |
|
63 | dc.SetPen(wx.TRANSPARENT_PEN) | |
|
64 | dc.SetFont(attr.GetFont()) | |
|
65 | dc.DrawRectangleRect(rect) | |
|
66 | dc.SetClippingRect(rect) | |
|
67 | # Format the text | |
|
68 | if align == -1: # left alignment | |
|
69 | (width, height) = dc.GetTextExtent(str(text)) | |
|
70 | x = rect[0]+1 | |
|
71 | y = rect[1]+0.5*(rect[3]-height) | |
|
72 | ||
|
73 | for (style, part) in text: | |
|
74 | if isSelected: | |
|
75 | fg = grid.GetSelectionForeground() | |
|
76 | else: | |
|
77 | fg = self.colormap[style.fg] | |
|
78 | dc.SetTextForeground(fg) | |
|
79 | (w, h) = dc.GetTextExtent(part) | |
|
80 | dc.DrawText(part, x, y) | |
|
81 | x += w | |
|
82 | elif align == 0: # center alignment | |
|
83 | (width, height) = dc.GetTextExtent(str(text)) | |
|
84 | x = rect[0]+0.5*(rect[2]-width) | |
|
85 | y = rect[1]+0.5*(rect[3]-height) | |
|
86 | for (style, part) in text: | |
|
87 | if isSelected: | |
|
88 | fg = grid.GetSelectionForeground() | |
|
89 | else: | |
|
90 | fg = self.colormap[style.fg] | |
|
91 | dc.SetTextForeground(fg) | |
|
92 | (w, h) = dc.GetTextExtent(part) | |
|
93 | dc.DrawText(part, x, y) | |
|
94 | x += w | |
|
95 | else: # right alignment | |
|
96 | (width, height) = dc.GetTextExtent(str(text)) | |
|
97 | x = rect[0]+rect[2]-1 | |
|
98 | y = rect[1]+0.5*(rect[3]-height) | |
|
99 | for (style, part) in reversed(text): | |
|
100 | (w, h) = dc.GetTextExtent(part) | |
|
101 | x -= w | |
|
102 | if isSelected: | |
|
103 | fg = grid.GetSelectionForeground() | |
|
104 | else: | |
|
105 | fg = self.colormap[style.fg] | |
|
106 | dc.SetTextForeground(fg) | |
|
107 | dc.DrawText(part, x, y) | |
|
108 | dc.DestroyClippingRegion() | |
|
109 | ||
|
110 | def Clone(self): | |
|
111 | return IGridRenderer(self.table) | |
|
112 | ||
|
113 | ||
|
114 | class IGridTable(wx.grid.PyGridTableBase): | |
|
115 | # The data table for the ``IGridGrid``. Some dirty tricks were used here: | |
|
116 | # ``GetValue()`` does not get any values (or at least it does not return | |
|
117 | # anything, accessing the values is done by the renderer) | |
|
118 | # but rather tries to fetch the objects which were requested into the table. | |
|
119 | # General behaviour is: Fetch the first X objects. If the user scrolls down | |
|
120 | # to the last object another bunch of X objects is fetched (if possible) | |
|
121 | def __init__(self, input, fontsize, *attrs): | |
|
122 | wx.grid.PyGridTableBase.__init__(self) | |
|
123 | self.input = input | |
|
124 | self.iterator = ipipe.xiter(input) | |
|
125 | self.items = [] | |
|
126 | self.hiddenattrs = [] | |
|
127 | self.attrs = attrs | |
|
128 | self.displayattrs = [] | |
|
129 | self.fetch(1) | |
|
130 | self.sizing = False | |
|
131 | self.fontsize = fontsize | |
|
132 | ||
|
133 | def GetAttr(self, *args): | |
|
134 | attr = wx.grid.GridCellAttr() | |
|
135 | attr.SetFont(wx.Font(self.fontsize, wx.TELETYPE, wx.NORMAL, wx.NORMAL)) | |
|
136 | return attr | |
|
137 | ||
|
138 | def GetNumberRows(self): | |
|
139 | return len(self.items) | |
|
140 | ||
|
141 | def GetNumberCols(self): | |
|
142 | return len(self.displayattrs) | |
|
143 | ||
|
144 | def GetColLabelValue(self, col): | |
|
145 | if col < len(self.displayattrs): | |
|
146 | return self.displayattrs[col].name() | |
|
147 | else: | |
|
148 | return "" | |
|
149 | ||
|
150 | def GetRowLabelValue(self, row): | |
|
151 | return str(row) | |
|
152 | ||
|
153 | def IsEmptyCell(self, row, col): | |
|
154 | return False | |
|
155 | ||
|
156 | def fetch(self, count): | |
|
157 | # Try to fill ``self.items`` with at least ``count`` objects. | |
|
158 | have = len(self.items) | |
|
159 | work = False | |
|
160 | while self.iterator is not None and have < count: | |
|
161 | try: | |
|
162 | item = self.iterator.next() | |
|
163 | except StopIteration: | |
|
164 | self.iterator = None | |
|
165 | break | |
|
166 | except (KeyboardInterrupt, SystemExit): | |
|
167 | raise | |
|
168 | except Exception, exc: | |
|
169 | have += 1 | |
|
170 | self.items.append(exc) | |
|
171 | work = True | |
|
172 | self.iterator = None | |
|
173 | break | |
|
174 | else: | |
|
175 | have += 1 | |
|
176 | self.items.append(item) | |
|
177 | work = True | |
|
178 | if work: | |
|
179 | self.calcdisplayattrs() | |
|
180 | ||
|
181 | def calcdisplayattrs(self): | |
|
182 | # Calculate which attributes are available from the objects that are | |
|
183 | # currently visible on screen (and store it in ``self.displayattrs``) | |
|
184 | attrs = set() | |
|
185 | self.displayattrs = [] | |
|
186 | if self.attrs: | |
|
187 | # If the browser object specifies a fixed list of attributes, | |
|
188 | # simply use it (removing hidden attributes). | |
|
189 | for attr in self.attrs: | |
|
190 | attr = ipipe.upgradexattr(attr) | |
|
191 | if attr not in attrs and attr not in self.hiddenattrs: | |
|
192 | self.displayattrs.append(attr) | |
|
193 | attrs.add(attr) | |
|
194 | else: | |
|
195 | endy = len(self.items) | |
|
196 | for i in xrange(endy): | |
|
197 | for attr in ipipe.xattrs(self.items[i]): | |
|
198 | attr = ipipe.upgradexattr(attr) | |
|
199 | if attr not in attrs and attr not in self.hiddenattrs: | |
|
200 | self.displayattrs.append(attr) | |
|
201 | attrs.add(attr) | |
|
202 | ||
|
203 | def GetValue(self, row, col): | |
|
204 | # some kind of dummy-function: does not return anything but ""; | |
|
205 | # (The value isn't use anyway) | |
|
206 | # its main task is to trigger the fetch of new objects | |
|
207 | had_cols = self.displayattrs[:] | |
|
208 | had_rows = len(self.items) | |
|
209 | if row == had_rows - 1 and self.iterator is not None and not self.sizing: | |
|
210 | self.fetch(row + 20) | |
|
211 | have_rows = len(self.items) | |
|
212 | have_cols = len(self.displayattrs) | |
|
213 | if have_rows > had_rows: | |
|
214 | msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, have_rows - had_rows) | |
|
215 | self.GetView().ProcessTableMessage(msg) | |
|
216 | self.sizing = True | |
|
217 | self.GetView().AutoSizeColumns(False) | |
|
218 | self.sizing = False | |
|
219 | if row >= have_rows: | |
|
220 | return "" | |
|
221 | if self.displayattrs != had_cols: | |
|
222 | msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED, have_cols - len(had_cols)) | |
|
223 | self.GetView().ProcessTableMessage(msg) | |
|
224 | return "" | |
|
225 | ||
|
226 | def SetValue(self, row, col, value): | |
|
227 | pass | |
|
228 | ||
|
229 | ||
|
230 | class IGridGrid(wx.grid.Grid): | |
|
231 | # The actual grid | |
|
232 | # all methods for selecting/sorting/picking/... data are implemented here | |
|
233 | def __init__(self, panel, input, *attrs): | |
|
234 | wx.grid.Grid.__init__(self, panel) | |
|
235 | fontsize = 9 | |
|
236 | self.input = input | |
|
237 | self.table = IGridTable(self.input, fontsize, *attrs) | |
|
238 | self.SetTable(self.table, True) | |
|
239 | self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows) | |
|
240 | self.SetDefaultRenderer(IGridRenderer(self.table)) | |
|
241 | self.EnableEditing(False) | |
|
242 | self.Bind(wx.EVT_KEY_DOWN, self.key_pressed) | |
|
243 | self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.cell_doubleclicked) | |
|
244 | self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_DCLICK, self.label_doubleclicked) | |
|
245 | self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_label_leftclick) | |
|
246 | self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self._on_selected_range) | |
|
247 | self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_selected_cell) | |
|
248 | self.current_selection = set() | |
|
249 | self.maxchars = 200 | |
|
250 | ||
|
251 | def on_label_leftclick(self, event): | |
|
252 | event.Skip() | |
|
253 | ||
|
254 | def error_output(self, text): | |
|
255 | wx.Bell() | |
|
256 | frame = self.GetParent().GetParent().GetParent() | |
|
257 | frame.SetStatusText(text) | |
|
258 | ||
|
259 | def _on_selected_range(self, event): | |
|
260 | # Internal update to the selection tracking lists | |
|
261 | if event.Selecting(): | |
|
262 | # adding to the list... | |
|
263 | self.current_selection.update(xrange(event.GetTopRow(), event.GetBottomRow()+1)) | |
|
264 | else: | |
|
265 | # removal from list | |
|
266 | for index in xrange( event.GetTopRow(), event.GetBottomRow()+1): | |
|
267 | self.current_selection.discard(index) | |
|
268 | event.Skip() | |
|
269 | ||
|
270 | def _on_selected_cell(self, event): | |
|
271 | # Internal update to the selection tracking list | |
|
272 | self.current_selection = set([event.GetRow()]) | |
|
273 | event.Skip() | |
|
274 | ||
|
275 | def sort(self, key, reverse=False): | |
|
276 | """ | |
|
277 | Sort the current list of items using the key function ``key``. If | |
|
278 | ``reverse`` is true the sort order is reversed. | |
|
279 | """ | |
|
280 | row = self.GetGridCursorRow() | |
|
281 | col = self.GetGridCursorCol() | |
|
282 | curitem = self.table.items[row] # Remember where the cursor is now | |
|
283 | # Sort items | |
|
284 | def realkey(item): | |
|
285 | return key(item) | |
|
286 | try: | |
|
287 | self.table.items = ipipe.deque(sorted(self.table.items, key=realkey, reverse=reverse)) | |
|
288 | except TypeError, exc: | |
|
289 | self.error_output("Exception encountered: %s" % exc) | |
|
290 | return | |
|
291 | # Find out where the object under the cursor went | |
|
292 | for (i, item) in enumerate(self.table.items): | |
|
293 | if item is curitem: | |
|
294 | self.SetGridCursor(i,col) | |
|
295 | self.MakeCellVisible(i,col) | |
|
296 | self.Refresh() | |
|
297 | ||
|
298 | def sortattrasc(self): | |
|
299 | """ | |
|
300 | Sort in ascending order; sorting criteria is the current attribute | |
|
301 | """ | |
|
302 | col = self.GetGridCursorCol() | |
|
303 | attr = self.table.displayattrs[col] | |
|
304 | frame = self.GetParent().GetParent().GetParent() | |
|
305 | if attr is ipipe.noitem: | |
|
306 | self.error_output("no column under cursor") | |
|
307 | return | |
|
308 | frame.SetStatusText("sort by %s (ascending)" % attr.name()) | |
|
309 | def key(item): | |
|
310 | try: | |
|
311 | return attr.value(item) | |
|
312 | except (KeyboardInterrupt, SystemExit): | |
|
313 | raise | |
|
314 | except Exception: | |
|
315 | return None | |
|
316 | self.sort(key) | |
|
317 | ||
|
318 | def sortattrdesc(self): | |
|
319 | """ | |
|
320 | Sort in descending order; sorting criteria is the current attribute | |
|
321 | """ | |
|
322 | col = self.GetGridCursorCol() | |
|
323 | attr = self.table.displayattrs[col] | |
|
324 | frame = self.GetParent().GetParent().GetParent() | |
|
325 | if attr is ipipe.noitem: | |
|
326 | self.error_output("no column under cursor") | |
|
327 | return | |
|
328 | frame.SetStatusText("sort by %s (descending)" % attr.name()) | |
|
329 | def key(item): | |
|
330 | try: | |
|
331 | return attr.value(item) | |
|
332 | except (KeyboardInterrupt, SystemExit): | |
|
333 | raise | |
|
334 | except Exception: | |
|
335 | return None | |
|
336 | self.sort(key, reverse=True) | |
|
337 | ||
|
338 | def label_doubleclicked(self, event): | |
|
339 | row = event.GetRow() | |
|
340 | col = event.GetCol() | |
|
341 | if col == -1: | |
|
342 | self.enter(row) | |
|
343 | ||
|
344 | def _getvalue(self, row, col): | |
|
345 | """ | |
|
346 | Gets the text which is displayed at ``(row, col)`` | |
|
347 | """ | |
|
348 | try: | |
|
349 | value = self.table.displayattrs[col].value(self.table.items[row]) | |
|
350 | (align, width, text) = ipipe.xformat(value, "cell", self.maxchars) | |
|
351 | except IndexError: | |
|
352 | raise IndexError | |
|
353 | except Exception, exc: | |
|
354 | (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars) | |
|
355 | return text | |
|
356 | ||
|
357 | def search(self, searchtext, startrow=0, startcol=0, search_forward=True): | |
|
358 | """ | |
|
359 | search for ``searchtext``, starting in ``(startrow, startcol)``; | |
|
360 | if ``search_forward`` is true the direction is "forward" | |
|
361 | """ | |
|
362 | row = startrow | |
|
363 | searchtext = searchtext.lower() | |
|
364 | if search_forward: | |
|
365 | while True: | |
|
366 | for col in xrange(startcol, self.table.GetNumberCols()): | |
|
367 | try: | |
|
368 | foo = self.table.GetValue(row, col) | |
|
369 | text = self._getvalue(row, col) | |
|
370 | if searchtext in text.string().lower(): | |
|
371 | self.SetGridCursor(row, col) | |
|
372 | self.MakeCellVisible(row, col) | |
|
373 | return | |
|
374 | except IndexError: | |
|
375 | return | |
|
376 | startcol = 0 | |
|
377 | row += 1 | |
|
378 | else: | |
|
379 | while True: | |
|
380 | for col in xrange(startcol, -1, -1): | |
|
381 | try: | |
|
382 | foo = self.table.GetValue(row, col) | |
|
383 | text = self._getvalue(row, col) | |
|
384 | if searchtext in text.string().lower(): | |
|
385 | self.SetGridCursor(row, col) | |
|
386 | self.MakeCellVisible(row, col) | |
|
387 | return | |
|
388 | except IndexError: | |
|
389 | return | |
|
390 | startcol = self.table.GetNumberCols()-1 | |
|
391 | row -= 1 | |
|
392 | ||
|
393 | def key_pressed(self, event): | |
|
394 | """ | |
|
395 | Maps pressed keys to functions | |
|
396 | """ | |
|
397 | frame = self.GetParent().GetParent().GetParent() | |
|
398 | frame.SetStatusText("") | |
|
399 | sh = event.ShiftDown() | |
|
400 | ctrl = event.ControlDown() | |
|
401 | ||
|
402 | keycode = event.GetKeyCode() | |
|
403 | if keycode == ord("P"): | |
|
404 | row = self.GetGridCursorRow() | |
|
405 | if event.ShiftDown(): | |
|
406 | col = self.GetGridCursorCol() | |
|
407 | self.pickattr(row, col) | |
|
408 | else: | |
|
409 | self.pick(row) | |
|
410 | elif keycode == ord("M"): | |
|
411 | if ctrl: | |
|
412 | col = self.GetGridCursorCol() | |
|
413 | self.pickrowsattr(sorted(self.current_selection), col) | |
|
414 | else: | |
|
415 | self.pickrows(sorted(self.current_selection)) | |
|
416 | elif keycode in (wx.WXK_BACK, wx.WXK_DELETE, ord("X")) and not (ctrl or sh): | |
|
417 | self.delete_current_notebook() | |
|
418 | elif keycode == ord("E") and not (ctrl or sh): | |
|
419 | row = self.GetGridCursorRow() | |
|
420 | self.enter(row) | |
|
421 | elif keycode == ord("E") and sh and not ctrl: | |
|
422 | row = self.GetGridCursorRow() | |
|
423 | col = self.GetGridCursorCol() | |
|
424 | self.enterattr(row, col) | |
|
425 | elif keycode == ord("E") and ctrl: | |
|
426 | row = self.GetGridCursorRow() | |
|
427 | self.SetGridCursor(row, self.GetNumberCols()-1) | |
|
428 | elif keycode == wx.WXK_HOME or (keycode == ord("A") and ctrl): | |
|
429 | row = self.GetGridCursorRow() | |
|
430 | self.SetGridCursor(row, 0) | |
|
431 | elif keycode == ord("C") and sh: | |
|
432 | col = self.GetGridCursorCol() | |
|
433 | attr = self.table.displayattrs[col] | |
|
434 | returnobj = [] | |
|
435 | for i in xrange(self.GetNumberRows()): | |
|
436 | returnobj.append(self.table.displayattrs[col].value(self.table.items[i])) | |
|
437 | self.quit(returnobj) | |
|
438 | elif keycode in (wx.WXK_ESCAPE, ord("Q")) and not (ctrl or sh): | |
|
439 | self.quit() | |
|
440 | elif keycode == ord("<"): | |
|
441 | row = self.GetGridCursorRow() | |
|
442 | col = self.GetGridCursorCol() | |
|
443 | if not event.ShiftDown(): | |
|
444 | newcol = col - 1 | |
|
445 | if newcol >= 0: | |
|
446 | self.SetGridCursor(row, col - 1) | |
|
447 | else: | |
|
448 | newcol = col + 1 | |
|
449 | if newcol < self.GetNumberCols(): | |
|
450 | self.SetGridCursor(row, col + 1) | |
|
451 | elif keycode == ord("D"): | |
|
452 | col = self.GetGridCursorCol() | |
|
453 | row = self.GetGridCursorRow() | |
|
454 | if not sh: | |
|
455 | self.detail(row, col) | |
|
456 | else: | |
|
457 | self.detail_attr(row, col) | |
|
458 | elif keycode == ord("F") and ctrl: | |
|
459 | frame.enter_searchtext(event) | |
|
460 | elif keycode == wx.WXK_F3: | |
|
461 | if sh: | |
|
462 | frame.find_previous(event) | |
|
463 | else: | |
|
464 | frame.find_next(event) | |
|
465 | elif keycode == ord("V"): | |
|
466 | if sh: | |
|
467 | self.sortattrdesc() | |
|
468 | else: | |
|
469 | self.sortattrasc() | |
|
470 | else: | |
|
471 | event.Skip() | |
|
472 | ||
|
473 | def delete_current_notebook(self): | |
|
474 | """ | |
|
475 | deletes the current notebook tab | |
|
476 | """ | |
|
477 | panel = self.GetParent() | |
|
478 | nb = panel.GetParent() | |
|
479 | current = nb.GetSelection() | |
|
480 | count = nb.GetPageCount() | |
|
481 | if count > 1: | |
|
482 | for i in xrange(count-1, current-1, -1): | |
|
483 | nb.DeletePage(i) | |
|
484 | nb.GetCurrentPage().grid.SetFocus() | |
|
485 | else: | |
|
486 | frame = nb.GetParent() | |
|
487 | frame.SetStatusText("This is the last level!") | |
|
488 | ||
|
489 | def _doenter(self, value, *attrs): | |
|
490 | """ | |
|
491 | "enter" a special item resulting in a new notebook tab | |
|
492 | """ | |
|
493 | panel = self.GetParent() | |
|
494 | nb = panel.GetParent() | |
|
495 | frame = nb.GetParent() | |
|
496 | current = nb.GetSelection() | |
|
497 | count = nb.GetPageCount() | |
|
498 | try: # if we want to enter something non-iterable, e.g. a function | |
|
499 | if current + 1 == count and value is not self.input: # we have an event in the last tab | |
|
500 | frame._add_notebook(value, *attrs) | |
|
501 | elif value != self.input: # we have to delete all tabs newer than [panel] first | |
|
502 | for i in xrange(count-1, current, -1): # some tabs don't close if we don't close in *reverse* order | |
|
503 | nb.DeletePage(i) | |
|
504 | frame._add_notebook(value) | |
|
505 | except TypeError, exc: | |
|
506 | if exc.__class__.__module__ == "exceptions": | |
|
507 | msg = "%s: %s" % (exc.__class__.__name__, exc) | |
|
508 | else: | |
|
509 | msg = "%s.%s: %s" % (exc.__class__.__module__, exc.__class__.__name__, exc) | |
|
510 | frame.SetStatusText(msg) | |
|
511 | ||
|
512 | def enterattr(self, row, col): | |
|
513 | try: | |
|
514 | attr = self.table.displayattrs[col] | |
|
515 | value = attr.value(self.table.items[row]) | |
|
516 | except Exception, exc: | |
|
517 | self.error_output(str(exc)) | |
|
518 | else: | |
|
519 | self._doenter(value) | |
|
520 | ||
|
521 | def enter(self, row): | |
|
522 | try: | |
|
523 | value = self.table.items[row] | |
|
524 | except Exception, exc: | |
|
525 | self.error_output(str(exc)) | |
|
526 | else: | |
|
527 | self._doenter(value) | |
|
528 | ||
|
529 | def detail(self, row, col): | |
|
530 | """ | |
|
531 | shows a detail-view of the current cell | |
|
532 | """ | |
|
533 | try: | |
|
534 | attr = self.table.displayattrs[col] | |
|
535 | item = self.table.items[row] | |
|
536 | except Exception, exc: | |
|
537 | self.error_output(str(exc)) | |
|
538 | else: | |
|
539 | attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")] | |
|
540 | self._doenter(attrs) | |
|
541 | ||
|
542 | def detail_attr(self, row, col): | |
|
543 | try: | |
|
544 | attr = self.table.displayattrs[col] | |
|
545 | item = attr.value(self.table.items[row]) | |
|
546 | except Exception, exc: | |
|
547 | self.error_output(str(exc)) | |
|
548 | else: | |
|
549 | attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")] | |
|
550 | self._doenter(attrs) | |
|
551 | ||
|
552 | def quit(self, returnobj=None): | |
|
553 | """ | |
|
554 | quit | |
|
555 | """ | |
|
556 | frame = self.GetParent().GetParent().GetParent() | |
|
557 | frame.parent.returnobj = returnobj | |
|
558 | frame.Close() | |
|
559 | frame.Destroy() | |
|
560 | ||
|
561 | def cell_doubleclicked(self, event): | |
|
562 | self.enterattr(event.GetRow(), event.GetCol()) | |
|
563 | ||
|
564 | def pick(self, row): | |
|
565 | """ | |
|
566 | pick a single row and return to the IPython prompt | |
|
567 | """ | |
|
568 | try: | |
|
569 | value = self.table.items[row] | |
|
570 | except Exception, exc: | |
|
571 | self.error_output(str(exc)) | |
|
572 | else: | |
|
573 | self.quit(value) | |
|
574 | ||
|
575 | def pickrows(self, rows): | |
|
576 | """ | |
|
577 | pick multiple rows and return to the IPython prompt | |
|
578 | """ | |
|
579 | try: | |
|
580 | value = [self.table.items[row] for row in rows] | |
|
581 | except Exception, exc: | |
|
582 | self.error_output(str(exc)) | |
|
583 | else: | |
|
584 | self.quit(value) | |
|
585 | ||
|
586 | def pickrowsattr(self, rows, col): | |
|
587 | """" | |
|
588 | pick one column from multiple rows | |
|
589 | """ | |
|
590 | values = [] | |
|
591 | try: | |
|
592 | attr = self.table.displayattrs[col] | |
|
593 | for row in rows: | |
|
594 | try: | |
|
595 | values.append(attr.value(self.table.items[row])) | |
|
596 | except (SystemExit, KeyboardInterrupt): | |
|
597 | raise | |
|
598 | except Exception: | |
|
599 | raise #pass | |
|
600 | except Exception, exc: | |
|
601 | self.error_output(str(exc)) | |
|
602 | else: | |
|
603 | self.quit(values) | |
|
604 | ||
|
605 | def pickattr(self, row, col): | |
|
606 | try: | |
|
607 | attr = self.table.displayattrs[col] | |
|
608 | value = attr.value(self.table.items[row]) | |
|
609 | except Exception, exc: | |
|
610 | self.error_output(str(exc)) | |
|
611 | else: | |
|
612 | self.quit(value) | |
|
613 | ||
|
614 | ||
|
615 | class IGridPanel(wx.Panel): | |
|
616 | # Each IGridPanel contains an IGridGrid | |
|
617 | def __init__(self, parent, input, *attrs): | |
|
618 | wx.Panel.__init__(self, parent, -1) | |
|
619 | self.grid = IGridGrid(self, input, *attrs) | |
|
620 | sizer = wx.BoxSizer(wx.VERTICAL) | |
|
621 | sizer.Add(self.grid, proportion=1, flag=wx.EXPAND | wx.ALL, border=10) | |
|
622 | self.SetSizer(sizer) | |
|
623 | sizer.Fit(self) | |
|
624 | sizer.SetSizeHints(self) | |
|
625 | ||
|
626 | ||
|
627 | class IGridHTMLHelp(wx.Frame): | |
|
628 | def __init__(self, parent, title, filename, size): | |
|
629 | wx.Frame.__init__(self, parent, -1, title, size=size) | |
|
630 | html = wx.html.HtmlWindow(self) | |
|
631 | if "gtk2" in wx.PlatformInfo: | |
|
632 | html.SetStandardFonts() | |
|
633 | html.LoadFile(filename) | |
|
634 | ||
|
635 | ||
|
636 | class IGridFrame(wx.Frame): | |
|
637 | maxtitlelen = 30 | |
|
638 | ||
|
639 | def __init__(self, parent, input): | |
|
640 | wx.Frame.__init__(self, None, title="IGrid", size=(640, 480)) | |
|
641 | self.menubar = wx.MenuBar() | |
|
642 | self.menucounter = 100 | |
|
643 | self.m_help = wx.Menu() | |
|
644 | self.m_search = wx.Menu() | |
|
645 | self.m_sort = wx.Menu() | |
|
646 | self.notebook = wx.Notebook(self, -1, style=0) | |
|
647 | self.statusbar = self.CreateStatusBar(1, wx.ST_SIZEGRIP) | |
|
648 | self.parent = parent | |
|
649 | self._add_notebook(input) | |
|
650 | self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) | |
|
651 | self.makemenu(self.m_sort, "&Sort (asc)", "Sort ascending", self.sortasc) | |
|
652 | self.makemenu(self.m_sort, "Sort (&desc)", "Sort descending", self.sortdesc) | |
|
653 | self.makemenu(self.m_help, "&Help", "Help", self.display_help) | |
|
654 | self.makemenu(self.m_help, "&Show help in browser", "Show help in browser", self.display_help_in_browser) | |
|
655 | self.makemenu(self.m_search, "&Find text", "Find text", self.enter_searchtext) | |
|
656 | self.makemenu(self.m_search, "Find by &expression", "Find by expression", self.enter_searchexpression) | |
|
657 | self.makemenu(self.m_search, "Find &next", "Find next", self.find_next) | |
|
658 | self.makemenu(self.m_search, "Find &previous", "Find previous", self.find_previous) | |
|
659 | self.menubar.Append(self.m_search, "&Find") | |
|
660 | self.menubar.Append(self.m_sort, "&Sort") | |
|
661 | self.menubar.Append(self.m_help, "&Help") | |
|
662 | self.SetMenuBar(self.menubar) | |
|
663 | self.searchtext = "" | |
|
664 | ||
|
665 | def sortasc(self, event): | |
|
666 | grid = self.notebook.GetPage(self.notebook.GetSelection()).grid | |
|
667 | grid.sortattrasc() | |
|
668 | ||
|
669 | def sortdesc(self, event): | |
|
670 | grid = self.notebook.GetPage(self.notebook.GetSelection()).grid | |
|
671 | grid.sortattrdesc() | |
|
672 | ||
|
673 | def find_previous(self, event): | |
|
674 | """ | |
|
675 | find previous occurrences | |
|
676 | """ | |
|
677 | if self.searchtext: | |
|
678 | grid = self.notebook.GetPage(self.notebook.GetSelection()).grid | |
|
679 | row = grid.GetGridCursorRow() | |
|
680 | col = grid.GetGridCursorCol() | |
|
681 | if col-1 >= 0: | |
|
682 | grid.search(self.searchtext, row, col-1, False) | |
|
683 | else: | |
|
684 | grid.search(self.searchtext, row-1, grid.table.GetNumberCols()-1, False) | |
|
685 | else: | |
|
686 | self.enter_searchtext(event) | |
|
687 | ||
|
688 | def find_next(self, event): | |
|
689 | """ | |
|
690 | find the next occurrence | |
|
691 | """ | |
|
692 | if self.searchtext: | |
|
693 | grid = self.notebook.GetPage(self.notebook.GetSelection()).grid | |
|
694 | row = grid.GetGridCursorRow() | |
|
695 | col = grid.GetGridCursorCol() | |
|
696 | if col+1 < grid.table.GetNumberCols(): | |
|
697 | grid.search(self.searchtext, row, col+1) | |
|
698 | else: | |
|
699 | grid.search(self.searchtext, row+1, 0) | |
|
700 | else: | |
|
701 | self.enter_searchtext(event) | |
|
702 | ||
|
703 | def display_help(self, event): | |
|
704 | """ | |
|
705 | Display a help dialog | |
|
706 | """ | |
|
707 | filename = os.path.join(os.path.dirname(__file__), "help.html") | |
|
708 | frm = IGridHTMLHelp(None, title="Help", filename=filename, size=wx.Size(600,400)) | |
|
709 | frm.Show() | |
|
710 | ||
|
711 | def display_help_in_browser(self, event): | |
|
712 | """ | |
|
713 | Show the help-HTML in a browser (as a ``HtmlWindow`` does not understand | |
|
714 | CSS this looks better) | |
|
715 | """ | |
|
716 | filename = urllib.pathname2url(os.path.abspath(os.path.join(os.path.dirname(__file__), "help.html"))) | |
|
717 | if not filename.startswith("file"): | |
|
718 | filename = "file:" + filename | |
|
719 | webbrowser.open(filename, new=1, autoraise=True) | |
|
720 | ||
|
721 | def enter_searchexpression(self, event): | |
|
722 | pass | |
|
723 | ||
|
724 | def makemenu(self, menu, label, help, cmd): | |
|
725 | menu.Append(self.menucounter, label, help) | |
|
726 | self.Bind(wx.EVT_MENU, cmd, id=self.menucounter) | |
|
727 | self.menucounter += 1 | |
|
728 | ||
|
729 | def _add_notebook(self, input, *attrs): | |
|
730 | # Adds another notebook which has the starting object ``input`` | |
|
731 | panel = IGridPanel(self.notebook, input, *attrs) | |
|
732 | text = str(ipipe.xformat(input, "header", self.maxtitlelen)[2]) | |
|
733 | if len(text) >= self.maxtitlelen: | |
|
734 | text = text[:self.maxtitlelen].rstrip(".") + "..." | |
|
735 | self.notebook.AddPage(panel, text, True) | |
|
736 | panel.grid.SetFocus() | |
|
737 | self.Layout() | |
|
738 | ||
|
739 | def OnCloseWindow(self, event): | |
|
740 | self.Destroy() | |
|
741 | ||
|
742 | def enter_searchtext(self, event): | |
|
743 | # Displays a dialog asking for the searchtext | |
|
744 | dlg = wx.TextEntryDialog(self, "Find:", "Find in list") | |
|
745 | if dlg.ShowModal() == wx.ID_OK: | |
|
746 | self.searchtext = dlg.GetValue() | |
|
747 | self.notebook.GetPage(self.notebook.GetSelection()).grid.search(self.searchtext, 0, 0) | |
|
748 | dlg.Destroy() | |
|
749 | ||
|
750 | ||
|
751 | class igrid(ipipe.Display): | |
|
752 | """ | |
|
753 | This is a wx-based display object that can be used instead of ``ibrowse`` | |
|
754 | (which is curses-based) or ``idump`` (which simply does a print). | |
|
755 | """ | |
|
756 | def display(self): | |
|
757 | self.returnobj = None | |
|
758 | app = wx.App() | |
|
759 | self.frame = IGridFrame(self, self.input) | |
|
760 | self.frame.Show() | |
|
761 | app.SetTopWindow(self.frame) | |
|
762 | self.frame.Raise() | |
|
763 | app.MainLoop() | |
|
764 | return self.returnobj |
@@ -1,2146 +1,2153 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 | 138 | "icsv", "ix", "ichain", "isort", "ifilter", "ieval", "ienum", "ienv", |
|
139 | 139 | "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 == "cell" or mode in "header" or mode == "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 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 | 1571 | class icsv(Pipe): |
|
1572 | 1572 | """ |
|
1573 | 1573 | This ``Pipe`` lists turn the input (with must be a pipe outputting lines |
|
1574 | 1574 | or an ``ifile``) into lines of CVS columns. |
|
1575 | 1575 | """ |
|
1576 | 1576 | def __init__(self, **csvargs): |
|
1577 | 1577 | """ |
|
1578 | 1578 | Create an ``icsv`` object. ``cvsargs`` will be passed through as |
|
1579 | 1579 | keyword arguments to ``cvs.reader()``. |
|
1580 | 1580 | """ |
|
1581 | 1581 | self.csvargs = csvargs |
|
1582 | 1582 | |
|
1583 | 1583 | def __iter__(self): |
|
1584 | 1584 | input = self.input |
|
1585 | 1585 | if isinstance(input, ifile): |
|
1586 | 1586 | input = input.open("rb") |
|
1587 | 1587 | reader = csv.reader(input, **self.csvargs) |
|
1588 | 1588 | for line in reader: |
|
1589 | 1589 | yield List(line) |
|
1590 | 1590 | |
|
1591 | 1591 | def __xrepr__(self, mode="default"): |
|
1592 | 1592 | yield (-1, False) |
|
1593 | 1593 | if mode == "header" or mode == "footer": |
|
1594 | 1594 | input = getattr(self, "input", None) |
|
1595 | 1595 | if input is not None: |
|
1596 | 1596 | for part in xrepr(input, mode): |
|
1597 | 1597 | yield part |
|
1598 | 1598 | yield (astyle.style_default, " | ") |
|
1599 | 1599 | yield (astyle.style_default, "%s(" % self.__class__.__name__) |
|
1600 | 1600 | for (i, (name, value)) in enumerate(self.csvargs.iteritems()): |
|
1601 | 1601 | if i: |
|
1602 | 1602 | yield (astyle.style_default, ", ") |
|
1603 | 1603 | yield (astyle.style_default, name) |
|
1604 | 1604 | yield (astyle.style_default, "=") |
|
1605 | 1605 | for part in xrepr(value, "default"): |
|
1606 | 1606 | yield part |
|
1607 | 1607 | yield (astyle.style_default, ")") |
|
1608 | 1608 | else: |
|
1609 | 1609 | yield (astyle.style_default, repr(self)) |
|
1610 | 1610 | |
|
1611 | 1611 | def __repr__(self): |
|
1612 | 1612 | args = ", ".join(["%s=%r" % item for item in self.csvargs.iteritems()]) |
|
1613 | 1613 | return "<%s.%s %s at 0x%x>" % \ |
|
1614 | 1614 | (self.__class__.__module__, self.__class__.__name__, args, id(self)) |
|
1615 | 1615 | |
|
1616 | 1616 | |
|
1617 | 1617 | class ix(Table): |
|
1618 | 1618 | """ |
|
1619 | 1619 | Execute a system command and list its output as lines |
|
1620 | 1620 | (similar to ``os.popen()``). |
|
1621 | 1621 | |
|
1622 | 1622 | Examples: |
|
1623 | 1623 | |
|
1624 | 1624 | >>> ix("ps x") |
|
1625 | 1625 | >>> ix("find .") | ifile |
|
1626 | 1626 | """ |
|
1627 | 1627 | def __init__(self, cmd): |
|
1628 | 1628 | self.cmd = cmd |
|
1629 | 1629 | self._pipeout = None |
|
1630 | 1630 | |
|
1631 | 1631 | def __iter__(self): |
|
1632 | 1632 | (_pipein, self._pipeout) = os.popen4(self.cmd) |
|
1633 | 1633 | _pipein.close() |
|
1634 | 1634 | for l in self._pipeout: |
|
1635 | 1635 | yield l.rstrip("\r\n") |
|
1636 | 1636 | self._pipeout.close() |
|
1637 | 1637 | self._pipeout = None |
|
1638 | 1638 | |
|
1639 | 1639 | def __del__(self): |
|
1640 | 1640 | if self._pipeout is not None and not self._pipeout.closed: |
|
1641 | 1641 | self._pipeout.close() |
|
1642 | 1642 | self._pipeout = None |
|
1643 | 1643 | |
|
1644 | 1644 | def __xrepr__(self, mode="default"): |
|
1645 | 1645 | if mode == "header" or mode == "footer": |
|
1646 | 1646 | yield (astyle.style_default, |
|
1647 | 1647 | "%s(%r)" % (self.__class__.__name__, self.cmd)) |
|
1648 | 1648 | else: |
|
1649 | 1649 | yield (astyle.style_default, repr(self)) |
|
1650 | 1650 | |
|
1651 | 1651 | def __repr__(self): |
|
1652 | 1652 | return "%s.%s(%r)" % \ |
|
1653 | 1653 | (self.__class__.__module__, self.__class__.__name__, self.cmd) |
|
1654 | 1654 | |
|
1655 | 1655 | |
|
1656 | 1656 | class ifilter(Pipe): |
|
1657 | 1657 | """ |
|
1658 | 1658 | Filter an input pipe. Only objects where an expression evaluates to true |
|
1659 | 1659 | (and doesn't raise an exception) are listed. |
|
1660 | 1660 | |
|
1661 | 1661 | Examples: |
|
1662 | 1662 | |
|
1663 | 1663 | >>> ils | ifilter("_.isfile() and size>1000") |
|
1664 | 1664 | >>> igrp | ifilter("len(mem)") |
|
1665 | 1665 | >>> sys.modules | ifilter(lambda _:_.value is not None) |
|
1666 | 1666 | """ |
|
1667 | 1667 | |
|
1668 | 1668 | def __init__(self, expr, globals=None, errors="raiseifallfail"): |
|
1669 | 1669 | """ |
|
1670 | 1670 | Create an ``ifilter`` object. ``expr`` can be a callable or a string |
|
1671 | 1671 | containing an expression. ``globals`` will be used as the global |
|
1672 | 1672 | namespace for calling string expressions (defaulting to IPython's |
|
1673 | 1673 | user namespace). ``errors`` specifies how exception during evaluation |
|
1674 | 1674 | of ``expr`` are handled: |
|
1675 | 1675 | |
|
1676 | 1676 | * ``drop``: drop all items that have errors; |
|
1677 | 1677 | |
|
1678 | 1678 | * ``keep``: keep all items that have errors; |
|
1679 | 1679 | |
|
1680 | 1680 | * ``keeperror``: keep the exception of all items that have errors; |
|
1681 | 1681 | |
|
1682 | 1682 | * ``raise``: raise the exception; |
|
1683 | 1683 | |
|
1684 | 1684 | * ``raiseifallfail``: raise the first exception if all items have errors; |
|
1685 | 1685 | otherwise drop those with errors (this is the default). |
|
1686 | 1686 | """ |
|
1687 | 1687 | self.expr = expr |
|
1688 | 1688 | self.globals = globals |
|
1689 | 1689 | self.errors = errors |
|
1690 | 1690 | |
|
1691 | 1691 | def __iter__(self): |
|
1692 | 1692 | if callable(self.expr): |
|
1693 | 1693 | test = self.expr |
|
1694 | 1694 | else: |
|
1695 | 1695 | g = getglobals(self.globals) |
|
1696 | 1696 | expr = compile(self.expr, "ipipe-expression", "eval") |
|
1697 | 1697 | def test(item): |
|
1698 | 1698 | return eval(expr, g, AttrNamespace(item)) |
|
1699 | 1699 | |
|
1700 | 1700 | ok = 0 |
|
1701 | 1701 | exc_info = None |
|
1702 | 1702 | for item in xiter(self.input): |
|
1703 | 1703 | try: |
|
1704 | 1704 | if test(item): |
|
1705 | 1705 | yield item |
|
1706 | 1706 | ok += 1 |
|
1707 | 1707 | except (KeyboardInterrupt, SystemExit): |
|
1708 | 1708 | raise |
|
1709 | 1709 | except Exception, exc: |
|
1710 | 1710 | if self.errors == "drop": |
|
1711 | 1711 | pass # Ignore errors |
|
1712 | 1712 | elif self.errors == "keep": |
|
1713 | 1713 | yield item |
|
1714 | 1714 | elif self.errors == "keeperror": |
|
1715 | 1715 | yield exc |
|
1716 | 1716 | elif self.errors == "raise": |
|
1717 | 1717 | raise |
|
1718 | 1718 | elif self.errors == "raiseifallfail": |
|
1719 | 1719 | if exc_info is None: |
|
1720 | 1720 | exc_info = sys.exc_info() |
|
1721 | 1721 | if not ok and exc_info is not None: |
|
1722 | 1722 | raise exc_info[0], exc_info[1], exc_info[2] |
|
1723 | 1723 | |
|
1724 | 1724 | def __xrepr__(self, mode="default"): |
|
1725 | 1725 | if mode == "header" or mode == "footer": |
|
1726 | 1726 | input = getattr(self, "input", None) |
|
1727 | 1727 | if input is not None: |
|
1728 | 1728 | for part in xrepr(input, mode): |
|
1729 | 1729 | yield part |
|
1730 | 1730 | yield (astyle.style_default, " | ") |
|
1731 | 1731 | yield (astyle.style_default, "%s(" % self.__class__.__name__) |
|
1732 | 1732 | for part in xrepr(self.expr, "default"): |
|
1733 | 1733 | yield part |
|
1734 | 1734 | yield (astyle.style_default, ")") |
|
1735 | 1735 | else: |
|
1736 | 1736 | yield (astyle.style_default, repr(self)) |
|
1737 | 1737 | |
|
1738 | 1738 | def __repr__(self): |
|
1739 | 1739 | return "<%s.%s expr=%r at 0x%x>" % \ |
|
1740 | 1740 | (self.__class__.__module__, self.__class__.__name__, |
|
1741 | 1741 | self.expr, id(self)) |
|
1742 | 1742 | |
|
1743 | 1743 | |
|
1744 | 1744 | class ieval(Pipe): |
|
1745 | 1745 | """ |
|
1746 | 1746 | Evaluate an expression for each object in the input pipe. |
|
1747 | 1747 | |
|
1748 | 1748 | Examples: |
|
1749 | 1749 | |
|
1750 | 1750 | >>> ils | ieval("_.abspath()") |
|
1751 | 1751 | >>> sys.path | ieval(ifile) |
|
1752 | 1752 | """ |
|
1753 | 1753 | |
|
1754 | 1754 | def __init__(self, expr, globals=None, errors="raiseifallfail"): |
|
1755 | 1755 | """ |
|
1756 | 1756 | Create an ``ieval`` object. ``expr`` can be a callable or a string |
|
1757 | 1757 | containing an expression. For the meaning of ``globals`` and |
|
1758 | 1758 | ``errors`` see ``ifilter``. |
|
1759 | 1759 | """ |
|
1760 | 1760 | self.expr = expr |
|
1761 | 1761 | self.globals = globals |
|
1762 | 1762 | self.errors = errors |
|
1763 | 1763 | |
|
1764 | 1764 | def __iter__(self): |
|
1765 | 1765 | if callable(self.expr): |
|
1766 | 1766 | do = self.expr |
|
1767 | 1767 | else: |
|
1768 | 1768 | g = getglobals(self.globals) |
|
1769 | 1769 | expr = compile(self.expr, "ipipe-expression", "eval") |
|
1770 | 1770 | def do(item): |
|
1771 | 1771 | return eval(expr, g, AttrNamespace(item)) |
|
1772 | 1772 | |
|
1773 | 1773 | ok = 0 |
|
1774 | 1774 | exc_info = None |
|
1775 | 1775 | for item in xiter(self.input): |
|
1776 | 1776 | try: |
|
1777 | 1777 | yield do(item) |
|
1778 | 1778 | except (KeyboardInterrupt, SystemExit): |
|
1779 | 1779 | raise |
|
1780 | 1780 | except Exception, exc: |
|
1781 | 1781 | if self.errors == "drop": |
|
1782 | 1782 | pass # Ignore errors |
|
1783 | 1783 | elif self.errors == "keep": |
|
1784 | 1784 | yield item |
|
1785 | 1785 | elif self.errors == "keeperror": |
|
1786 | 1786 | yield exc |
|
1787 | 1787 | elif self.errors == "raise": |
|
1788 | 1788 | raise |
|
1789 | 1789 | elif self.errors == "raiseifallfail": |
|
1790 | 1790 | if exc_info is None: |
|
1791 | 1791 | exc_info = sys.exc_info() |
|
1792 | 1792 | if not ok and exc_info is not None: |
|
1793 | 1793 | raise exc_info[0], exc_info[1], exc_info[2] |
|
1794 | 1794 | |
|
1795 | 1795 | def __xrepr__(self, mode="default"): |
|
1796 | 1796 | if mode == "header" or mode == "footer": |
|
1797 | 1797 | input = getattr(self, "input", None) |
|
1798 | 1798 | if input is not None: |
|
1799 | 1799 | for part in xrepr(input, mode): |
|
1800 | 1800 | yield part |
|
1801 | 1801 | yield (astyle.style_default, " | ") |
|
1802 | 1802 | yield (astyle.style_default, "%s(" % self.__class__.__name__) |
|
1803 | 1803 | for part in xrepr(self.expr, "default"): |
|
1804 | 1804 | yield part |
|
1805 | 1805 | yield (astyle.style_default, ")") |
|
1806 | 1806 | else: |
|
1807 | 1807 | yield (astyle.style_default, repr(self)) |
|
1808 | 1808 | |
|
1809 | 1809 | def __repr__(self): |
|
1810 | 1810 | return "<%s.%s expr=%r at 0x%x>" % \ |
|
1811 | 1811 | (self.__class__.__module__, self.__class__.__name__, |
|
1812 | 1812 | self.expr, id(self)) |
|
1813 | 1813 | |
|
1814 | 1814 | |
|
1815 | 1815 | class ienum(Pipe): |
|
1816 | 1816 | """ |
|
1817 | 1817 | Enumerate the input pipe (i.e. wrap each input object in an object |
|
1818 | 1818 | with ``index`` and ``object`` attributes). |
|
1819 | 1819 | |
|
1820 | 1820 | Examples: |
|
1821 | 1821 | |
|
1822 | 1822 | >>> xrange(20) | ieval("_,_*_") | ienum | ifilter("index % 2 == 0") | ieval("object") |
|
1823 | 1823 | """ |
|
1824 | 1824 | def __iter__(self): |
|
1825 | 1825 | fields = ("index", "object") |
|
1826 | 1826 | for (index, object) in enumerate(xiter(self.input)): |
|
1827 | 1827 | yield Fields(fields, index=index, object=object) |
|
1828 | 1828 | |
|
1829 | 1829 | |
|
1830 | 1830 | class isort(Pipe): |
|
1831 | 1831 | """ |
|
1832 | 1832 | Sorts the input pipe. |
|
1833 | 1833 | |
|
1834 | 1834 | Examples: |
|
1835 | 1835 | |
|
1836 | 1836 | >>> ils | isort("size") |
|
1837 | 1837 | >>> ils | isort("_.isdir(), _.lower()", reverse=True) |
|
1838 | 1838 | """ |
|
1839 | 1839 | |
|
1840 | 1840 | def __init__(self, key=None, globals=None, reverse=False): |
|
1841 | 1841 | """ |
|
1842 | 1842 | Create an ``isort`` object. ``key`` can be a callable or a string |
|
1843 | 1843 | containing an expression (or ``None`` in which case the items |
|
1844 | 1844 | themselves will be sorted). If ``reverse`` is true the sort order |
|
1845 | 1845 | will be reversed. For the meaning of ``globals`` see ``ifilter``. |
|
1846 | 1846 | """ |
|
1847 | 1847 | self.key = key |
|
1848 | 1848 | self.globals = globals |
|
1849 | 1849 | self.reverse = reverse |
|
1850 | 1850 | |
|
1851 | 1851 | def __iter__(self): |
|
1852 | 1852 | if self.key is None: |
|
1853 | 1853 | items = sorted(xiter(self.input), reverse=self.reverse) |
|
1854 | 1854 | elif callable(self.key): |
|
1855 | 1855 | items = sorted(xiter(self.input), key=self.key, reverse=self.reverse) |
|
1856 | 1856 | else: |
|
1857 | 1857 | g = getglobals(self.globals) |
|
1858 | 1858 | key = compile(self.key, "ipipe-expression", "eval") |
|
1859 | 1859 | def realkey(item): |
|
1860 | 1860 | return eval(key, g, AttrNamespace(item)) |
|
1861 | 1861 | items = sorted(xiter(self.input), key=realkey, reverse=self.reverse) |
|
1862 | 1862 | for item in items: |
|
1863 | 1863 | yield item |
|
1864 | 1864 | |
|
1865 | 1865 | def __xrepr__(self, mode="default"): |
|
1866 | 1866 | if mode == "header" or mode == "footer": |
|
1867 | 1867 | input = getattr(self, "input", None) |
|
1868 | 1868 | if input is not None: |
|
1869 | 1869 | for part in xrepr(input, mode): |
|
1870 | 1870 | yield part |
|
1871 | 1871 | yield (astyle.style_default, " | ") |
|
1872 | 1872 | yield (astyle.style_default, "%s(" % self.__class__.__name__) |
|
1873 | 1873 | for part in xrepr(self.key, "default"): |
|
1874 | 1874 | yield part |
|
1875 | 1875 | if self.reverse: |
|
1876 | 1876 | yield (astyle.style_default, ", ") |
|
1877 | 1877 | for part in xrepr(True, "default"): |
|
1878 | 1878 | yield part |
|
1879 | 1879 | yield (astyle.style_default, ")") |
|
1880 | 1880 | else: |
|
1881 | 1881 | yield (astyle.style_default, repr(self)) |
|
1882 | 1882 | |
|
1883 | 1883 | def __repr__(self): |
|
1884 | 1884 | return "<%s.%s key=%r reverse=%r at 0x%x>" % \ |
|
1885 | 1885 | (self.__class__.__module__, self.__class__.__name__, |
|
1886 | 1886 | self.key, self.reverse, id(self)) |
|
1887 | 1887 | |
|
1888 | 1888 | |
|
1889 | 1889 | tab = 3 # for expandtabs() |
|
1890 | 1890 | |
|
1891 | 1891 | def _format(field): |
|
1892 | 1892 | if isinstance(field, str): |
|
1893 | 1893 | text = repr(field.expandtabs(tab))[1:-1] |
|
1894 | 1894 | elif isinstance(field, unicode): |
|
1895 | 1895 | text = repr(field.expandtabs(tab))[2:-1] |
|
1896 | 1896 | elif isinstance(field, datetime.datetime): |
|
1897 | 1897 | # Don't use strftime() here, as this requires year >= 1900 |
|
1898 | 1898 | text = "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \ |
|
1899 | 1899 | (field.year, field.month, field.day, |
|
1900 | 1900 | field.hour, field.minute, field.second, field.microsecond) |
|
1901 | 1901 | elif isinstance(field, datetime.date): |
|
1902 | 1902 | text = "%04d-%02d-%02d" % (field.year, field.month, field.day) |
|
1903 | 1903 | else: |
|
1904 | 1904 | text = repr(field) |
|
1905 | 1905 | return text |
|
1906 | 1906 | |
|
1907 | 1907 | |
|
1908 | 1908 | class Display(object): |
|
1909 | 1909 | class __metaclass__(type): |
|
1910 | 1910 | def __ror__(self, input): |
|
1911 | 1911 | return input | self() |
|
1912 | 1912 | |
|
1913 | 1913 | def __ror__(self, input): |
|
1914 | 1914 | self.input = input |
|
1915 | 1915 | return self |
|
1916 | 1916 | |
|
1917 | 1917 | def display(self): |
|
1918 | 1918 | pass |
|
1919 | 1919 | |
|
1920 | 1920 | |
|
1921 | 1921 | class iless(Display): |
|
1922 | 1922 | cmd = "less --quit-if-one-screen --LONG-PROMPT --LINE-NUMBERS --chop-long-lines --shift=8 --RAW-CONTROL-CHARS" |
|
1923 | 1923 | |
|
1924 | 1924 | def display(self): |
|
1925 | 1925 | try: |
|
1926 | 1926 | pager = os.popen(self.cmd, "w") |
|
1927 | 1927 | try: |
|
1928 | 1928 | for item in xiter(self.input): |
|
1929 | 1929 | first = False |
|
1930 | 1930 | for attr in xattrs(item, "default"): |
|
1931 | 1931 | if first: |
|
1932 | 1932 | first = False |
|
1933 | 1933 | else: |
|
1934 | 1934 | pager.write(" ") |
|
1935 | 1935 | attr = upgradexattr(attr) |
|
1936 | 1936 | if not isinstance(attr, SelfDescriptor): |
|
1937 | 1937 | pager.write(attr.name()) |
|
1938 | 1938 | pager.write("=") |
|
1939 | 1939 | pager.write(str(attr.value(item))) |
|
1940 | 1940 | pager.write("\n") |
|
1941 | 1941 | finally: |
|
1942 | 1942 | pager.close() |
|
1943 | 1943 | except Exception, exc: |
|
1944 | 1944 | print "%s: %s" % (exc.__class__.__name__, str(exc)) |
|
1945 | 1945 | |
|
1946 | 1946 | |
|
1947 | 1947 | def xformat(value, mode, maxlength): |
|
1948 | 1948 | align = None |
|
1949 | 1949 | full = True |
|
1950 | 1950 | width = 0 |
|
1951 | 1951 | text = astyle.Text() |
|
1952 | 1952 | for (style, part) in xrepr(value, mode): |
|
1953 | 1953 | # only consider the first result |
|
1954 | 1954 | if align is None: |
|
1955 | 1955 | if isinstance(style, int): |
|
1956 | 1956 | # (style, text) really is (alignment, stop) |
|
1957 | 1957 | align = style |
|
1958 | 1958 | full = part |
|
1959 | 1959 | continue |
|
1960 | 1960 | else: |
|
1961 | 1961 | align = -1 |
|
1962 | 1962 | full = True |
|
1963 | 1963 | if not isinstance(style, int): |
|
1964 | 1964 | text.append((style, part)) |
|
1965 | 1965 | width += len(part) |
|
1966 | 1966 | if width >= maxlength and not full: |
|
1967 | 1967 | text.append((astyle.style_ellisis, "...")) |
|
1968 | 1968 | width += 3 |
|
1969 | 1969 | break |
|
1970 | 1970 | if align is None: # default to left alignment |
|
1971 | 1971 | align = -1 |
|
1972 | 1972 | return (align, width, text) |
|
1973 | 1973 | |
|
1974 | 1974 | |
|
1975 | 1975 | class idump(Display): |
|
1976 | 1976 | # The approximate maximum length of a column entry |
|
1977 | 1977 | maxattrlength = 200 |
|
1978 | 1978 | |
|
1979 | 1979 | # Style for column names |
|
1980 | 1980 | style_header = astyle.Style.fromstr("white:black:bold") |
|
1981 | 1981 | |
|
1982 | 1982 | def __init__(self, *attrs): |
|
1983 | 1983 | self.attrs = [upgradexattr(attr) for attr in attrs] |
|
1984 | 1984 | self.headerpadchar = " " |
|
1985 | 1985 | self.headersepchar = "|" |
|
1986 | 1986 | self.datapadchar = " " |
|
1987 | 1987 | self.datasepchar = "|" |
|
1988 | 1988 | |
|
1989 | 1989 | def display(self): |
|
1990 | 1990 | stream = genutils.Term.cout |
|
1991 | 1991 | allattrs = [] |
|
1992 | 1992 | attrset = set() |
|
1993 | 1993 | colwidths = {} |
|
1994 | 1994 | rows = [] |
|
1995 | 1995 | for item in xiter(self.input): |
|
1996 | 1996 | row = {} |
|
1997 | 1997 | attrs = self.attrs |
|
1998 | 1998 | if not attrs: |
|
1999 | 1999 | attrs = xattrs(item, "default") |
|
2000 | 2000 | for attr in attrs: |
|
2001 | 2001 | if attr not in attrset: |
|
2002 | 2002 | allattrs.append(attr) |
|
2003 | 2003 | attrset.add(attr) |
|
2004 | 2004 | colwidths[attr] = len(attr.name()) |
|
2005 | 2005 | try: |
|
2006 | 2006 | value = attr.value(item) |
|
2007 | 2007 | except (KeyboardInterrupt, SystemExit): |
|
2008 | 2008 | raise |
|
2009 | 2009 | except Exception, exc: |
|
2010 | 2010 | value = exc |
|
2011 | 2011 | (align, width, text) = xformat(value, "cell", self.maxattrlength) |
|
2012 | 2012 | colwidths[attr] = max(colwidths[attr], width) |
|
2013 | 2013 | # remember alignment, length and colored parts |
|
2014 | 2014 | row[attr] = (align, width, text) |
|
2015 | 2015 | rows.append(row) |
|
2016 | 2016 | |
|
2017 | 2017 | stream.write("\n") |
|
2018 | 2018 | for (i, attr) in enumerate(allattrs): |
|
2019 | 2019 | attrname = attr.name() |
|
2020 | 2020 | self.style_header(attrname).write(stream) |
|
2021 | 2021 | spc = colwidths[attr] - len(attrname) |
|
2022 | 2022 | if i < len(colwidths)-1: |
|
2023 | 2023 | stream.write(self.headerpadchar*spc) |
|
2024 | 2024 | stream.write(self.headersepchar) |
|
2025 | 2025 | stream.write("\n") |
|
2026 | 2026 | |
|
2027 | 2027 | for row in rows: |
|
2028 | 2028 | for (i, attr) in enumerate(allattrs): |
|
2029 | 2029 | (align, width, text) = row[attr] |
|
2030 | 2030 | spc = colwidths[attr] - width |
|
2031 | 2031 | if align == -1: |
|
2032 | 2032 | text.write(stream) |
|
2033 | 2033 | if i < len(colwidths)-1: |
|
2034 | 2034 | stream.write(self.datapadchar*spc) |
|
2035 | 2035 | elif align == 0: |
|
2036 | 2036 | spc = colwidths[attr] - width |
|
2037 | 2037 | spc1 = spc//2 |
|
2038 | 2038 | spc2 = spc-spc1 |
|
2039 | 2039 | stream.write(self.datapadchar*spc1) |
|
2040 | 2040 | text.write(stream) |
|
2041 | 2041 | if i < len(colwidths)-1: |
|
2042 | 2042 | stream.write(self.datapadchar*spc2) |
|
2043 | 2043 | else: |
|
2044 | 2044 | stream.write(self.datapadchar*spc) |
|
2045 | 2045 | text.write(stream) |
|
2046 | 2046 | if i < len(colwidths)-1: |
|
2047 | 2047 | stream.write(self.datasepchar) |
|
2048 | 2048 | stream.write("\n") |
|
2049 | 2049 | |
|
2050 | 2050 | |
|
2051 | 2051 | class AttributeDetail(Table): |
|
2052 | 2052 | """ |
|
2053 | 2053 | ``AttributeDetail`` objects are use for displaying a detailed list of object |
|
2054 | 2054 | attributes. |
|
2055 | 2055 | """ |
|
2056 | 2056 | def __init__(self, object, descriptor): |
|
2057 | 2057 | self.object = object |
|
2058 | 2058 | self.descriptor = descriptor |
|
2059 | 2059 | |
|
2060 | 2060 | def __iter__(self): |
|
2061 | 2061 | return self.descriptor.iter(self.object) |
|
2062 | 2062 | |
|
2063 | 2063 | def name(self): |
|
2064 | 2064 | return self.descriptor.name() |
|
2065 | 2065 | |
|
2066 | 2066 | def attrtype(self): |
|
2067 | 2067 | return self.descriptor.attrtype(self.object) |
|
2068 | 2068 | |
|
2069 | 2069 | def valuetype(self): |
|
2070 | 2070 | return self.descriptor.valuetype(self.object) |
|
2071 | 2071 | |
|
2072 | 2072 | def doc(self): |
|
2073 | 2073 | return self.descriptor.doc(self.object) |
|
2074 | 2074 | |
|
2075 | 2075 | def shortdoc(self): |
|
2076 | 2076 | return self.descriptor.shortdoc(self.object) |
|
2077 | 2077 | |
|
2078 | 2078 | def value(self): |
|
2079 | 2079 | return self.descriptor.value(self.object) |
|
2080 | 2080 | |
|
2081 | 2081 | def __xattrs__(self, mode="default"): |
|
2082 | 2082 | attrs = ("name()", "attrtype()", "valuetype()", "value()", "shortdoc()") |
|
2083 | 2083 | if mode == "detail": |
|
2084 | 2084 | attrs += ("doc()",) |
|
2085 | 2085 | return attrs |
|
2086 | 2086 | |
|
2087 | 2087 | def __xrepr__(self, mode="default"): |
|
2088 | 2088 | yield (-1, True) |
|
2089 | 2089 | valuetype = self.valuetype() |
|
2090 | 2090 | if valuetype is not noitem: |
|
2091 | 2091 | for part in xrepr(valuetype): |
|
2092 | 2092 | yield part |
|
2093 | 2093 | yield (astyle.style_default, " ") |
|
2094 | 2094 | yield (astyle.style_default, self.attrtype()) |
|
2095 | 2095 | yield (astyle.style_default, " ") |
|
2096 | 2096 | yield (astyle.style_default, self.name()) |
|
2097 | 2097 | yield (astyle.style_default, " of ") |
|
2098 | 2098 | for part in xrepr(self.object): |
|
2099 | 2099 | yield part |
|
2100 | 2100 | |
|
2101 | 2101 | |
|
2102 | 2102 | try: |
|
2103 |
from i |
|
|
2103 | from igrid import igrid | |
|
2104 | 2104 | except ImportError: |
|
2105 | # No curses (probably Windows) => use ``idump`` as the default display. | |
|
2106 | defaultdisplay = idump | |
|
2105 | # no wx | |
|
2106 | try: | |
|
2107 | from ibrowse import ibrowse | |
|
2108 | except ImportError: | |
|
2109 | # No curses (probably Windows) => use ``idump`` as the default display. | |
|
2110 | defaultdisplay = idump | |
|
2111 | else: | |
|
2112 | defaultdisplay = ibrowse | |
|
2113 | __all__.append("ibrowse") | |
|
2107 | 2114 | else: |
|
2108 |
defaultdisplay = i |
|
|
2109 |
__all__.append("i |
|
|
2115 | defaultdisplay = igrid | |
|
2116 | __all__.append("igrid") | |
|
2110 | 2117 | |
|
2111 | 2118 | |
|
2112 | 2119 | # If we're running under IPython, install an IPython displayhook that |
|
2113 | 2120 | # returns the object from Display.display(), else install a displayhook |
|
2114 | 2121 | # directly as sys.displayhook |
|
2115 | 2122 | api = None |
|
2116 | 2123 | if ipapi is not None: |
|
2117 | 2124 | try: |
|
2118 | 2125 | api = ipapi.get() |
|
2119 | 2126 | except AttributeError: |
|
2120 | 2127 | pass |
|
2121 | 2128 | |
|
2122 | 2129 | if api is not None: |
|
2123 | 2130 | def displayhook(self, obj): |
|
2124 | 2131 | if isinstance(obj, type) and issubclass(obj, Table): |
|
2125 | 2132 | obj = obj() |
|
2126 | 2133 | if isinstance(obj, Table): |
|
2127 | 2134 | obj = obj | defaultdisplay |
|
2128 | 2135 | if isinstance(obj, Display): |
|
2129 | 2136 | return obj.display() |
|
2130 | 2137 | else: |
|
2131 | 2138 | raise ipapi.TryNext |
|
2132 | 2139 | api.set_hook("result_display", displayhook) |
|
2133 | 2140 | else: |
|
2134 | 2141 | def installdisplayhook(): |
|
2135 | 2142 | _originalhook = sys.displayhook |
|
2136 | 2143 | def displayhook(obj): |
|
2137 | 2144 | if isinstance(obj, type) and issubclass(obj, Table): |
|
2138 | 2145 | obj = obj() |
|
2139 | 2146 | if isinstance(obj, Table): |
|
2140 | 2147 | obj = obj | defaultdisplay |
|
2141 | 2148 | if isinstance(obj, Display): |
|
2142 | 2149 | return obj.display() |
|
2143 | 2150 | else: |
|
2144 | 2151 | _originalhook(obj) |
|
2145 | 2152 | sys.displayhook = displayhook |
|
2146 | 2153 | 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