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