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

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

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