##// END OF EJS Templates
* IPython/Extensions/ipipe.py: Rename XAttr to AttributeDetail...
walter.doerwald -
Show More

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

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