##// END OF EJS Templates
ipapi.py => core/ipapi.py and imports updated.
Brian Granger -
Show More

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

@@ -1,87 +1,87 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ IPython extension: add %clear magic """
3 3
4 import IPython.ipapi
4 from IPython.core import ipapi
5 5 import gc
6 ip = IPython.ipapi.get()
6 ip = ipapi.get()
7 7
8 8 def clear_f(self,arg):
9 9 """ Clear various data (e.g. stored history data)
10 10
11 11 %clear in - clear input history
12 12 %clear out - clear output history
13 13 %clear shadow_compress - Compresses shadow history (to speed up ipython)
14 14 %clear shadow_nuke - permanently erase all entries in shadow history
15 15 %clear dhist - clear dir history
16 16 %clear array - clear only variables that are NumPy arrays
17 17
18 18 Examples:
19 19
20 20 In [1]: clear in
21 21 Flushing input history
22 22
23 23 In [2]: clear shadow_compress
24 24 Compressing shadow history
25 25
26 26 In [3]: clear shadow_nuke
27 27 Erased all keys from shadow history
28 28
29 29 In [4]: clear dhist
30 30 Clearing directory history
31 31 """
32 32
33 33 api = self.getapi()
34 34 user_ns = self.user_ns # local lookup, heavily used
35 35
36 36
37 37 for target in arg.split():
38 38
39 39 if target == 'out':
40 40 print "Flushing output cache (%d entries)" % len(user_ns['_oh'])
41 41 self.outputcache.flush()
42 42
43 43 elif target == 'in':
44 44 print "Flushing input history"
45 45 pc = self.outputcache.prompt_count + 1
46 46 for n in range(1, pc):
47 47 key = '_i'+`n`
48 48 user_ns.pop(key,None)
49 49 try:
50 50 del user_ns[key]
51 51 except: pass
52 52 # must be done in-place
53 53 self.input_hist[:] = ['\n'] * pc
54 54 self.input_hist_raw[:] = ['\n'] * pc
55 55
56 56 elif target == 'array':
57 57 # Support cleaning up numpy arrays
58 58 try:
59 59 from numpy import ndarray
60 60 # This must be done with items and not iteritems because we're
61 61 # going to modify the dict in-place.
62 62 for x,val in user_ns.items():
63 63 if isinstance(val,ndarray):
64 64 del user_ns[x]
65 65 except AttributeError:
66 66 print "Clear array only works if Numpy is available."
67 67
68 68 elif target == 'shadow_compress':
69 69 print "Compressing shadow history"
70 70 api.db.hcompress('shadowhist')
71 71
72 72 elif target == 'shadow_nuke':
73 73 print "Erased all keys from shadow history "
74 74 for k in ip.db.keys('shadowhist/*'):
75 75 del ip.db[k]
76 76
77 77 elif target == 'dhist':
78 78 print "Clearing directory history"
79 79 del user_ns['_dh'][:]
80 80
81 81 gc.collect()
82 82
83 83 # Activate the extension
84 84 ip.expose_magic("clear",clear_f)
85 85 import ipy_completers
86 86 ipy_completers.quick_completer(
87 87 '%clear','in out shadow_nuke shadow_compress dhist')
@@ -1,90 +1,90 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ %env magic command for storing environment variables persistently
3 3 """
4 4
5 import IPython.ipapi
6 ip = IPython.ipapi.get()
5 from IPython.core import ipapi
6 ip = ipapi.get()
7 7
8 8 import os,sys
9 9
10 10 def restore_env(self):
11 11 ip = self.getapi()
12 12 env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []})
13 13 for k,v in env['set'].items():
14 14 os.environ[k] = v
15 15 for k,v in env['add']:
16 16 os.environ[k] = os.environ.get(k,"") + v
17 17 for k,v in env['pre']:
18 18 os.environ[k] = v + os.environ.get(k,"")
19 raise IPython.ipapi.TryNext
19 raise ipapi.TryNext
20 20
21 21 ip.set_hook('late_startup_hook', restore_env)
22 22
23 23 def persist_env(self, parameter_s=''):
24 24 """ Store environment variables persistently
25 25
26 26 IPython remembers the values across sessions, which is handy to avoid
27 27 editing startup files.
28 28
29 29 %env - Show all environment variables
30 30 %env VISUAL=jed - set VISUAL to jed
31 31 %env PATH+=;/foo - append ;foo to PATH
32 32 %env PATH+=;/bar - also append ;bar to PATH
33 33 %env PATH-=/wbin; - prepend /wbin; to PATH
34 34 %env -d VISUAL - forget VISUAL persistent val
35 35 %env -p - print all persistent env modifications
36 36 """
37 37
38 38 if not parameter_s.strip():
39 39 return os.environ.data
40 40
41 41 ip = self.getapi()
42 42 db = ip.db
43 43 env = ip.db.get('stored_env', {'set' : {}, 'add' : [], 'pre' : []})
44 44
45 45 if parameter_s.startswith('-p'):
46 46 return env
47 47
48 48 elif parameter_s.startswith('-d'):
49 49 parts = (parameter_s.split()[1], '<del>')
50 50
51 51 else:
52 52 parts = parameter_s.strip().split('=')
53 53
54 54 if len(parts) == 2:
55 55 k,v = [p.strip() for p in parts]
56 56
57 57 if v == '<del>':
58 58 if k in env['set']:
59 59 del env['set'][k]
60 60 env['add'] = [el for el in env['add'] if el[0] != k]
61 61 env['pre'] = [el for el in env['pre'] if el[0] != k]
62 62
63 63 print "Forgot '%s' (for next session)" % k
64 64
65 65 elif k.endswith('+'):
66 66 k = k[:-1]
67 67 env['add'].append((k,v))
68 68 os.environ[k] += v
69 69 print k,"after append =",os.environ[k]
70 70 elif k.endswith('-'):
71 71 k = k[:-1]
72 72 env['pre'].append((k,v))
73 73 os.environ[k] = v + os.environ.get(k,"")
74 74 print k,"after prepend =",os.environ[k]
75 75
76 76
77 77 else:
78 78 env['set'][k] = v
79 79 print "Setting",k,"to",v
80 80 os.environ[k] = v
81 81
82 82 db['stored_env'] = env
83 83
84 84 def env_completer(self,event):
85 85 """ Custom completer that lists all env vars """
86 86 return os.environ.keys()
87 87
88 88 ip.expose_magic('env', persist_env)
89 89 ip.set_hook('complete_command',env_completer, str_key = '%env')
90 90
@@ -1,63 +1,63 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ IPython extension: new prefilters for output grabbing
3 3
4 4 Provides
5 5
6 6 var = %magic blah blah
7 7
8 8 var = !ls
9 9 """
10 10
11 import IPython.ipapi
11 from IPython.core import ipapi
12 12 from IPython.utils.genutils import *
13 13
14 ip = IPython.ipapi.get()
14 ip = ipapi.get()
15 15
16 16 import re
17 17
18 18 def hnd_magic(line,mo):
19 19 """ Handle a = %mymagic blah blah """
20 20 #cmd = genutils.make_quoted_expr(mo.group('syscmd'))
21 21 #mag = 'ipmagic
22 22 #return "%s = %s"
23 23 var = mo.group('varname')
24 24 cmd = mo.group('cmd')
25 25 expr = make_quoted_expr(cmd)
26 26 return itpl('$var = _ip.magic($expr)')
27 27
28 28 def hnd_syscmd(line,mo):
29 29 """ Handle a = !ls """
30 30 #cmd = genutils.make_quoted_expr(mo.group('syscmd'))
31 31 #mag = 'ipmagic
32 32 #return "%s = %s"
33 33 var = mo.group('varname')
34 34 cmd = mo.group('cmd')
35 35 expr = make_quoted_expr(itpl("sc -l =$cmd"))
36 36 return itpl('$var = _ip.magic($expr)')
37 37
38 38 def install_re_handler(pat, hnd):
39 39 ip.meta.re_prefilters.append((re.compile(pat), hnd))
40 40
41 41 def init_handlers():
42 42
43 43 ip.meta.re_prefilters = []
44 44
45 45 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*%(?P<cmd>.*)',
46 46 hnd_magic
47 47 )
48 48
49 49 install_re_handler('(?P<varname>[\w\.]+)\s*=\s*!(?P<cmd>.*)',
50 50 hnd_syscmd
51 51 )
52 52
53 53 init_handlers()
54 54
55 55 def regex_prefilter_f(self,line):
56 56 for pat, handler in ip.meta.re_prefilters:
57 57 mo = pat.match(line)
58 58 if mo:
59 59 return handler(line,mo)
60 60
61 raise IPython.ipapi.TryNext
61 raise ipapi.TryNext
62 62
63 63 ip.set_hook('input_prefilter', regex_prefilter_f)
@@ -1,1767 +1,1767 b''
1 1 # -*- coding: iso-8859-1 -*-
2 2
3 3 import curses, fcntl, signal, struct, tty, textwrap, inspect
4 4
5 from IPython import ipapi
5 from IPython.coreZ import ipapi
6 6
7 7 import astyle, ipipe
8 8
9 9
10 10 # Python 2.3 compatibility
11 11 try:
12 12 set
13 13 except NameError:
14 14 import sets
15 15 set = sets.Set
16 16
17 17 # Python 2.3 compatibility
18 18 try:
19 19 sorted
20 20 except NameError:
21 21 from ipipe import sorted
22 22
23 23
24 24 class UnassignedKeyError(Exception):
25 25 """
26 26 Exception that is used for reporting unassigned keys.
27 27 """
28 28
29 29
30 30 class UnknownCommandError(Exception):
31 31 """
32 32 Exception that is used for reporting unknown commands (this should never
33 33 happen).
34 34 """
35 35
36 36
37 37 class CommandError(Exception):
38 38 """
39 39 Exception that is used for reporting that a command can't be executed.
40 40 """
41 41
42 42
43 43 class Keymap(dict):
44 44 """
45 45 Stores mapping of keys to commands.
46 46 """
47 47 def __init__(self):
48 48 self._keymap = {}
49 49
50 50 def __setitem__(self, key, command):
51 51 if isinstance(key, str):
52 52 for c in key:
53 53 dict.__setitem__(self, ord(c), command)
54 54 else:
55 55 dict.__setitem__(self, key, command)
56 56
57 57 def __getitem__(self, key):
58 58 if isinstance(key, str):
59 59 key = ord(key)
60 60 return dict.__getitem__(self, key)
61 61
62 62 def __detitem__(self, key):
63 63 if isinstance(key, str):
64 64 key = ord(key)
65 65 dict.__detitem__(self, key)
66 66
67 67 def register(self, command, *keys):
68 68 for key in keys:
69 69 self[key] = command
70 70
71 71 def get(self, key, default=None):
72 72 if isinstance(key, str):
73 73 key = ord(key)
74 74 return dict.get(self, key, default)
75 75
76 76 def findkey(self, command, default=ipipe.noitem):
77 77 for (key, commandcandidate) in self.iteritems():
78 78 if commandcandidate == command:
79 79 return key
80 80 if default is ipipe.noitem:
81 81 raise KeyError(command)
82 82 return default
83 83
84 84
85 85 class _BrowserCachedItem(object):
86 86 # This is used internally by ``ibrowse`` to store a item together with its
87 87 # marked status.
88 88 __slots__ = ("item", "marked")
89 89
90 90 def __init__(self, item):
91 91 self.item = item
92 92 self.marked = False
93 93
94 94
95 95 class _BrowserHelp(object):
96 96 style_header = astyle.Style.fromstr("yellow:black:bold")
97 97 # This is used internally by ``ibrowse`` for displaying the help screen.
98 98 def __init__(self, browser):
99 99 self.browser = browser
100 100
101 101 def __xrepr__(self, mode):
102 102 yield (-1, True)
103 103 if mode == "header" or mode == "footer":
104 104 yield (astyle.style_default, "ibrowse help screen")
105 105 else:
106 106 yield (astyle.style_default, repr(self))
107 107
108 108 def __iter__(self):
109 109 # Get reverse key mapping
110 110 allkeys = {}
111 111 for (key, cmd) in self.browser.keymap.iteritems():
112 112 allkeys.setdefault(cmd, []).append(key)
113 113
114 114 fields = ("key", "description")
115 115
116 116 commands = []
117 117 for name in dir(self.browser):
118 118 if name.startswith("cmd_"):
119 119 command = getattr(self.browser, name)
120 120 commands.append((inspect.getsourcelines(command)[-1], name[4:], command))
121 121 commands.sort()
122 122 commands = [(c[1], c[2]) for c in commands]
123 123 for (i, (name, command)) in enumerate(commands):
124 124 if i:
125 125 yield ipipe.Fields(fields, key="", description="")
126 126
127 127 description = command.__doc__
128 128 if description is None:
129 129 lines = []
130 130 else:
131 131 lines = [l.strip() for l in description.splitlines() if l.strip()]
132 132 description = "\n".join(lines)
133 133 lines = textwrap.wrap(description, 60)
134 134 keys = allkeys.get(name, [])
135 135
136 136 yield ipipe.Fields(fields, key="", description=astyle.Text((self.style_header, name)))
137 137 for i in xrange(max(len(keys), len(lines))):
138 138 try:
139 139 key = self.browser.keylabel(keys[i])
140 140 except IndexError:
141 141 key = ""
142 142 try:
143 143 line = lines[i]
144 144 except IndexError:
145 145 line = ""
146 146 yield ipipe.Fields(fields, key=key, description=line)
147 147
148 148
149 149 class _BrowserLevel(object):
150 150 # This is used internally to store the state (iterator, fetch items,
151 151 # position of cursor and screen, etc.) of one browser level
152 152 # An ``ibrowse`` object keeps multiple ``_BrowserLevel`` objects in
153 153 # a stack.
154 154 def __init__(self, browser, input, mainsizey, *attrs):
155 155 self.browser = browser
156 156 self.input = input
157 157 self.header = [x for x in ipipe.xrepr(input, "header") if not isinstance(x[0], int)]
158 158 # iterator for the input
159 159 self.iterator = ipipe.xiter(input)
160 160
161 161 # is the iterator exhausted?
162 162 self.exhausted = False
163 163
164 164 # attributes to be display (autodetected if empty)
165 165 self.attrs = attrs
166 166
167 167 # fetched items (+ marked flag)
168 168 self.items = ipipe.deque()
169 169
170 170 # Number of marked objects
171 171 self.marked = 0
172 172
173 173 # Vertical cursor position
174 174 self.cury = 0
175 175
176 176 # Horizontal cursor position
177 177 self.curx = 0
178 178
179 179 # Index of first data column
180 180 self.datastartx = 0
181 181
182 182 # Index of first data line
183 183 self.datastarty = 0
184 184
185 185 # height of the data display area
186 186 self.mainsizey = mainsizey
187 187
188 188 # width of the data display area (changes when scrolling)
189 189 self.mainsizex = 0
190 190
191 191 # Size of row number (changes when scrolling)
192 192 self.numbersizex = 0
193 193
194 194 # Attributes to display (in this order)
195 195 self.displayattrs = []
196 196
197 197 # index and attribute under the cursor
198 198 self.displayattr = (None, ipipe.noitem)
199 199
200 200 # Maps attributes to column widths
201 201 self.colwidths = {}
202 202
203 203 # Set of hidden attributes
204 204 self.hiddenattrs = set()
205 205
206 206 # This takes care of all the caches etc.
207 207 self.moveto(0, 0, refresh=True)
208 208
209 209 def fetch(self, count):
210 210 # Try to fill ``self.items`` with at least ``count`` objects.
211 211 have = len(self.items)
212 212 while not self.exhausted and have < count:
213 213 try:
214 214 item = self.iterator.next()
215 215 except StopIteration:
216 216 self.exhausted = True
217 217 break
218 218 except (KeyboardInterrupt, SystemExit):
219 219 raise
220 220 except Exception, exc:
221 221 have += 1
222 222 self.items.append(_BrowserCachedItem(exc))
223 223 self.exhausted = True
224 224 break
225 225 else:
226 226 have += 1
227 227 self.items.append(_BrowserCachedItem(item))
228 228
229 229 def calcdisplayattrs(self):
230 230 # Calculate which attributes are available from the objects that are
231 231 # currently visible on screen (and store it in ``self.displayattrs``)
232 232
233 233 attrs = set()
234 234 self.displayattrs = []
235 235 if self.attrs:
236 236 # If the browser object specifies a fixed list of attributes,
237 237 # simply use it (removing hidden attributes).
238 238 for attr in self.attrs:
239 239 attr = ipipe.upgradexattr(attr)
240 240 if attr not in attrs and attr not in self.hiddenattrs:
241 241 self.displayattrs.append(attr)
242 242 attrs.add(attr)
243 243 else:
244 244 endy = min(self.datastarty+self.mainsizey, len(self.items))
245 245 for i in xrange(self.datastarty, endy):
246 246 for attr in ipipe.xattrs(self.items[i].item, "default"):
247 247 if attr not in attrs and attr not in self.hiddenattrs:
248 248 self.displayattrs.append(attr)
249 249 attrs.add(attr)
250 250
251 251 def getrow(self, i):
252 252 # Return a dictionary with the attributes for the object
253 253 # ``self.items[i]``. Attribute names are taken from
254 254 # ``self.displayattrs`` so ``calcdisplayattrs()`` must have been
255 255 # called before.
256 256 row = {}
257 257 item = self.items[i].item
258 258 for attr in self.displayattrs:
259 259 try:
260 260 value = attr.value(item)
261 261 except (KeyboardInterrupt, SystemExit):
262 262 raise
263 263 except Exception, exc:
264 264 value = exc
265 265 # only store attribute if it exists (or we got an exception)
266 266 if value is not ipipe.noitem:
267 267 # remember alignment, length and colored text
268 268 row[attr] = ipipe.xformat(value, "cell", self.browser.maxattrlength)
269 269 return row
270 270
271 271 def calcwidths(self):
272 272 # Recalculate the displayed fields and their widths.
273 273 # ``calcdisplayattrs()'' must have been called and the cache
274 274 # for attributes of the objects on screen (``self.displayrows``)
275 275 # must have been filled. This sets ``self.colwidths`` which maps
276 276 # attribute descriptors to widths.
277 277 self.colwidths = {}
278 278 for row in self.displayrows:
279 279 for attr in self.displayattrs:
280 280 try:
281 281 length = row[attr][1]
282 282 except KeyError:
283 283 length = 0
284 284 # always add attribute to colwidths, even if it doesn't exist
285 285 if attr not in self.colwidths:
286 286 self.colwidths[attr] = len(attr.name())
287 287 newwidth = max(self.colwidths[attr], length)
288 288 self.colwidths[attr] = newwidth
289 289
290 290 # How many characters do we need to paint the largest item number?
291 291 self.numbersizex = len(str(self.datastarty+self.mainsizey-1))
292 292 # How must space have we got to display data?
293 293 self.mainsizex = self.browser.scrsizex-self.numbersizex-3
294 294 # width of all columns
295 295 self.datasizex = sum(self.colwidths.itervalues()) + len(self.colwidths)
296 296
297 297 def calcdisplayattr(self):
298 298 # Find out which attribute the cursor is on and store this
299 299 # information in ``self.displayattr``.
300 300 pos = 0
301 301 for (i, attr) in enumerate(self.displayattrs):
302 302 if pos+self.colwidths[attr] >= self.curx:
303 303 self.displayattr = (i, attr)
304 304 break
305 305 pos += self.colwidths[attr]+1
306 306 else:
307 307 self.displayattr = (None, ipipe.noitem)
308 308
309 309 def moveto(self, x, y, refresh=False):
310 310 # Move the cursor to the position ``(x,y)`` (in data coordinates,
311 311 # not in screen coordinates). If ``refresh`` is true, all cached
312 312 # values will be recalculated (e.g. because the list has been
313 313 # resorted, so screen positions etc. are no longer valid).
314 314 olddatastarty = self.datastarty
315 315 oldx = self.curx
316 316 oldy = self.cury
317 317 x = int(x+0.5)
318 318 y = int(y+0.5)
319 319 newx = x # remember where we wanted to move
320 320 newy = y # remember where we wanted to move
321 321
322 322 scrollbordery = min(self.browser.scrollbordery, self.mainsizey//2)
323 323 scrollborderx = min(self.browser.scrollborderx, self.mainsizex//2)
324 324
325 325 # Make sure that the cursor didn't leave the main area vertically
326 326 if y < 0:
327 327 y = 0
328 328 # try to get enough items to fill the screen
329 329 self.fetch(max(y+scrollbordery+1, self.mainsizey))
330 330 if y >= len(self.items):
331 331 y = max(0, len(self.items)-1)
332 332
333 333 # Make sure that the cursor stays on screen vertically
334 334 if y < self.datastarty+scrollbordery:
335 335 self.datastarty = max(0, y-scrollbordery)
336 336 elif y >= self.datastarty+self.mainsizey-scrollbordery:
337 337 self.datastarty = max(0, min(y-self.mainsizey+scrollbordery+1,
338 338 len(self.items)-self.mainsizey))
339 339
340 340 if refresh: # Do we need to refresh the complete display?
341 341 self.calcdisplayattrs()
342 342 endy = min(self.datastarty+self.mainsizey, len(self.items))
343 343 self.displayrows = map(self.getrow, xrange(self.datastarty, endy))
344 344 self.calcwidths()
345 345 # Did we scroll vertically => update displayrows
346 346 # and various other attributes
347 347 elif self.datastarty != olddatastarty:
348 348 # Recalculate which attributes we have to display
349 349 olddisplayattrs = self.displayattrs
350 350 self.calcdisplayattrs()
351 351 # If there are new attributes, recreate the cache
352 352 if self.displayattrs != olddisplayattrs:
353 353 endy = min(self.datastarty+self.mainsizey, len(self.items))
354 354 self.displayrows = map(self.getrow, xrange(self.datastarty, endy))
355 355 elif self.datastarty<olddatastarty: # we did scroll up
356 356 # drop rows from the end
357 357 del self.displayrows[self.datastarty-olddatastarty:]
358 358 # fetch new items
359 359 for i in xrange(min(olddatastarty, self.datastarty+self.mainsizey)-1,
360 360 self.datastarty-1, -1):
361 361 try:
362 362 row = self.getrow(i)
363 363 except IndexError:
364 364 # we didn't have enough objects to fill the screen
365 365 break
366 366 self.displayrows.insert(0, row)
367 367 else: # we did scroll down
368 368 # drop rows from the start
369 369 del self.displayrows[:self.datastarty-olddatastarty]
370 370 # fetch new items
371 371 for i in xrange(max(olddatastarty+self.mainsizey, self.datastarty),
372 372 self.datastarty+self.mainsizey):
373 373 try:
374 374 row = self.getrow(i)
375 375 except IndexError:
376 376 # we didn't have enough objects to fill the screen
377 377 break
378 378 self.displayrows.append(row)
379 379 self.calcwidths()
380 380
381 381 # Make sure that the cursor didn't leave the data area horizontally
382 382 if x < 0:
383 383 x = 0
384 384 elif x >= self.datasizex:
385 385 x = max(0, self.datasizex-1)
386 386
387 387 # Make sure that the cursor stays on screen horizontally
388 388 if x < self.datastartx+scrollborderx:
389 389 self.datastartx = max(0, x-scrollborderx)
390 390 elif x >= self.datastartx+self.mainsizex-scrollborderx:
391 391 self.datastartx = max(0, min(x-self.mainsizex+scrollborderx+1,
392 392 self.datasizex-self.mainsizex))
393 393
394 394 if x == oldx and y == oldy and (x != newx or y != newy): # couldn't move
395 395 self.browser.beep()
396 396 else:
397 397 self.curx = x
398 398 self.cury = y
399 399 self.calcdisplayattr()
400 400
401 401 def sort(self, key, reverse=False):
402 402 """
403 403 Sort the currently list of items using the key function ``key``. If
404 404 ``reverse`` is true the sort order is reversed.
405 405 """
406 406 curitem = self.items[self.cury] # Remember where the cursor is now
407 407
408 408 # Sort items
409 409 def realkey(item):
410 410 return key(item.item)
411 411 self.items = ipipe.deque(sorted(self.items, key=realkey, reverse=reverse))
412 412
413 413 # Find out where the object under the cursor went
414 414 cury = self.cury
415 415 for (i, item) in enumerate(self.items):
416 416 if item is curitem:
417 417 cury = i
418 418 break
419 419
420 420 self.moveto(self.curx, cury, refresh=True)
421 421
422 422 def refresh(self):
423 423 """
424 424 Restart iterating the input.
425 425 """
426 426 self.iterator = ipipe.xiter(self.input)
427 427 self.items.clear()
428 428 self.exhausted = False
429 429 self.datastartx = self.datastarty = 0
430 430 self.moveto(0, 0, refresh=True)
431 431
432 432 def refreshfind(self):
433 433 """
434 434 Restart iterating the input and go back to the same object as before
435 435 (if it can be found in the new iterator).
436 436 """
437 437 try:
438 438 oldobject = self.items[self.cury].item
439 439 except IndexError:
440 440 oldobject = ipipe.noitem
441 441 self.iterator = ipipe.xiter(self.input)
442 442 self.items.clear()
443 443 self.exhausted = False
444 444 while True:
445 445 self.fetch(len(self.items)+1)
446 446 if self.exhausted:
447 447 curses.beep()
448 448 self.datastartx = self.datastarty = 0
449 449 self.moveto(self.curx, 0, refresh=True)
450 450 break
451 451 if self.items[-1].item == oldobject:
452 452 self.datastartx = self.datastarty = 0
453 453 self.moveto(self.curx, len(self.items)-1, refresh=True)
454 454 break
455 455
456 456
457 457 class _CommandInput(object):
458 458 keymap = Keymap()
459 459 keymap.register("left", curses.KEY_LEFT)
460 460 keymap.register("right", curses.KEY_RIGHT)
461 461 keymap.register("home", curses.KEY_HOME, "\x01") # Ctrl-A
462 462 keymap.register("end", curses.KEY_END, "\x05") # Ctrl-E
463 463 # FIXME: What's happening here?
464 464 keymap.register("backspace", curses.KEY_BACKSPACE, "\x08\x7f")
465 465 keymap.register("delete", curses.KEY_DC)
466 466 keymap.register("delend", 0x0b) # Ctrl-K
467 467 keymap.register("execute", "\r\n")
468 468 keymap.register("up", curses.KEY_UP)
469 469 keymap.register("down", curses.KEY_DOWN)
470 470 keymap.register("incsearchup", curses.KEY_PPAGE)
471 471 keymap.register("incsearchdown", curses.KEY_NPAGE)
472 472 keymap.register("exit", "\x18"), # Ctrl-X
473 473
474 474 def __init__(self, prompt):
475 475 self.prompt = prompt
476 476 self.history = []
477 477 self.maxhistory = 100
478 478 self.input = ""
479 479 self.curx = 0
480 480 self.cury = -1 # blank line
481 481
482 482 def start(self):
483 483 self.input = ""
484 484 self.curx = 0
485 485 self.cury = -1 # blank line
486 486
487 487 def handlekey(self, browser, key):
488 488 cmdname = self.keymap.get(key, None)
489 489 if cmdname is not None:
490 490 cmdfunc = getattr(self, "cmd_%s" % cmdname, None)
491 491 if cmdfunc is not None:
492 492 return cmdfunc(browser)
493 493 curses.beep()
494 494 elif key != -1:
495 495 try:
496 496 char = chr(key)
497 497 except ValueError:
498 498 curses.beep()
499 499 else:
500 500 return self.handlechar(browser, char)
501 501
502 502 def handlechar(self, browser, char):
503 503 self.input = self.input[:self.curx] + char + self.input[self.curx:]
504 504 self.curx += 1
505 505 return True
506 506
507 507 def dohistory(self):
508 508 self.history.insert(0, self.input)
509 509 del self.history[:-self.maxhistory]
510 510
511 511 def cmd_backspace(self, browser):
512 512 if self.curx:
513 513 self.input = self.input[:self.curx-1] + self.input[self.curx:]
514 514 self.curx -= 1
515 515 return True
516 516 else:
517 517 curses.beep()
518 518
519 519 def cmd_delete(self, browser):
520 520 if self.curx<len(self.input):
521 521 self.input = self.input[:self.curx] + self.input[self.curx+1:]
522 522 return True
523 523 else:
524 524 curses.beep()
525 525
526 526 def cmd_delend(self, browser):
527 527 if self.curx<len(self.input):
528 528 self.input = self.input[:self.curx]
529 529 return True
530 530
531 531 def cmd_left(self, browser):
532 532 if self.curx:
533 533 self.curx -= 1
534 534 return True
535 535 else:
536 536 curses.beep()
537 537
538 538 def cmd_right(self, browser):
539 539 if self.curx < len(self.input):
540 540 self.curx += 1
541 541 return True
542 542 else:
543 543 curses.beep()
544 544
545 545 def cmd_home(self, browser):
546 546 if self.curx:
547 547 self.curx = 0
548 548 return True
549 549 else:
550 550 curses.beep()
551 551
552 552 def cmd_end(self, browser):
553 553 if self.curx < len(self.input):
554 554 self.curx = len(self.input)
555 555 return True
556 556 else:
557 557 curses.beep()
558 558
559 559 def cmd_up(self, browser):
560 560 if self.cury < len(self.history)-1:
561 561 self.cury += 1
562 562 self.input = self.history[self.cury]
563 563 self.curx = len(self.input)
564 564 return True
565 565 else:
566 566 curses.beep()
567 567
568 568 def cmd_down(self, browser):
569 569 if self.cury >= 0:
570 570 self.cury -= 1
571 571 if self.cury>=0:
572 572 self.input = self.history[self.cury]
573 573 else:
574 574 self.input = ""
575 575 self.curx = len(self.input)
576 576 return True
577 577 else:
578 578 curses.beep()
579 579
580 580 def cmd_incsearchup(self, browser):
581 581 prefix = self.input[:self.curx]
582 582 cury = self.cury
583 583 while True:
584 584 cury += 1
585 585 if cury >= len(self.history):
586 586 break
587 587 if self.history[cury].startswith(prefix):
588 588 self.input = self.history[cury]
589 589 self.cury = cury
590 590 return True
591 591 curses.beep()
592 592
593 593 def cmd_incsearchdown(self, browser):
594 594 prefix = self.input[:self.curx]
595 595 cury = self.cury
596 596 while True:
597 597 cury -= 1
598 598 if cury <= 0:
599 599 break
600 600 if self.history[cury].startswith(prefix):
601 601 self.input = self.history[self.cury]
602 602 self.cury = cury
603 603 return True
604 604 curses.beep()
605 605
606 606 def cmd_exit(self, browser):
607 607 browser.mode = "default"
608 608 return True
609 609
610 610 def cmd_execute(self, browser):
611 611 raise NotImplementedError
612 612
613 613
614 614 class _CommandGoto(_CommandInput):
615 615 def __init__(self):
616 616 _CommandInput.__init__(self, "goto object #")
617 617
618 618 def handlechar(self, browser, char):
619 619 # Only accept digits
620 620 if not "0" <= char <= "9":
621 621 curses.beep()
622 622 else:
623 623 return _CommandInput.handlechar(self, browser, char)
624 624
625 625 def cmd_execute(self, browser):
626 626 level = browser.levels[-1]
627 627 if self.input:
628 628 self.dohistory()
629 629 level.moveto(level.curx, int(self.input))
630 630 browser.mode = "default"
631 631 return True
632 632
633 633
634 634 class _CommandFind(_CommandInput):
635 635 def __init__(self):
636 636 _CommandInput.__init__(self, "find expression")
637 637
638 638 def cmd_execute(self, browser):
639 639 level = browser.levels[-1]
640 640 if self.input:
641 641 self.dohistory()
642 642 while True:
643 643 cury = level.cury
644 644 level.moveto(level.curx, cury+1)
645 645 if cury == level.cury:
646 646 curses.beep()
647 647 break # hit end
648 648 item = level.items[level.cury].item
649 649 try:
650 650 globals = ipipe.getglobals(None)
651 651 if eval(self.input, globals, ipipe.AttrNamespace(item)):
652 652 break # found something
653 653 except (KeyboardInterrupt, SystemExit):
654 654 raise
655 655 except Exception, exc:
656 656 browser.report(exc)
657 657 curses.beep()
658 658 break # break on error
659 659 browser.mode = "default"
660 660 return True
661 661
662 662
663 663 class _CommandFindBackwards(_CommandInput):
664 664 def __init__(self):
665 665 _CommandInput.__init__(self, "find backwards expression")
666 666
667 667 def cmd_execute(self, browser):
668 668 level = browser.levels[-1]
669 669 if self.input:
670 670 self.dohistory()
671 671 while level.cury:
672 672 level.moveto(level.curx, level.cury-1)
673 673 item = level.items[level.cury].item
674 674 try:
675 675 globals = ipipe.getglobals(None)
676 676 if eval(self.input, globals, ipipe.AttrNamespace(item)):
677 677 break # found something
678 678 except (KeyboardInterrupt, SystemExit):
679 679 raise
680 680 except Exception, exc:
681 681 browser.report(exc)
682 682 curses.beep()
683 683 break # break on error
684 684 else:
685 685 curses.beep()
686 686 browser.mode = "default"
687 687 return True
688 688
689 689
690 690 class ibrowse(ipipe.Display):
691 691 # Show this many lines from the previous screen when paging horizontally
692 692 pageoverlapx = 1
693 693
694 694 # Show this many lines from the previous screen when paging vertically
695 695 pageoverlapy = 1
696 696
697 697 # Start scrolling when the cursor is less than this number of columns
698 698 # away from the left or right screen edge
699 699 scrollborderx = 10
700 700
701 701 # Start scrolling when the cursor is less than this number of lines
702 702 # away from the top or bottom screen edge
703 703 scrollbordery = 5
704 704
705 705 # Accelerate by this factor when scrolling horizontally
706 706 acceleratex = 1.05
707 707
708 708 # Accelerate by this factor when scrolling vertically
709 709 acceleratey = 1.05
710 710
711 711 # The maximum horizontal scroll speed
712 712 # (as a factor of the screen width (i.e. 0.5 == half a screen width)
713 713 maxspeedx = 0.5
714 714
715 715 # The maximum vertical scroll speed
716 716 # (as a factor of the screen height (i.e. 0.5 == half a screen height)
717 717 maxspeedy = 0.5
718 718
719 719 # The maximum number of header lines for browser level
720 720 # if the nesting is deeper, only the innermost levels are displayed
721 721 maxheaders = 5
722 722
723 723 # The approximate maximum length of a column entry
724 724 maxattrlength = 200
725 725
726 726 # Styles for various parts of the GUI
727 727 style_objheadertext = astyle.Style.fromstr("white:black:bold|reverse")
728 728 style_objheadernumber = astyle.Style.fromstr("white:blue:bold|reverse")
729 729 style_objheaderobject = astyle.Style.fromstr("white:black:reverse")
730 730 style_colheader = astyle.Style.fromstr("blue:white:reverse")
731 731 style_colheaderhere = astyle.Style.fromstr("green:black:bold|reverse")
732 732 style_colheadersep = astyle.Style.fromstr("blue:black:reverse")
733 733 style_number = astyle.Style.fromstr("blue:white:reverse")
734 734 style_numberhere = astyle.Style.fromstr("green:black:bold|reverse")
735 735 style_sep = astyle.Style.fromstr("blue:black")
736 736 style_data = astyle.Style.fromstr("white:black")
737 737 style_datapad = astyle.Style.fromstr("blue:black:bold")
738 738 style_footer = astyle.Style.fromstr("black:white")
739 739 style_report = astyle.Style.fromstr("white:black")
740 740
741 741 # Column separator in header
742 742 headersepchar = "|"
743 743
744 744 # Character for padding data cell entries
745 745 datapadchar = "."
746 746
747 747 # Column separator in data area
748 748 datasepchar = "|"
749 749
750 750 # Character to use for "empty" cell (i.e. for non-existing attributes)
751 751 nodatachar = "-"
752 752
753 753 # Prompts for modes that require keyboard input
754 754 prompts = {
755 755 "goto": _CommandGoto(),
756 756 "find": _CommandFind(),
757 757 "findbackwards": _CommandFindBackwards()
758 758 }
759 759
760 760 # Maps curses key codes to "function" names
761 761 keymap = Keymap()
762 762 keymap.register("quit", "q")
763 763 keymap.register("up", curses.KEY_UP)
764 764 keymap.register("down", curses.KEY_DOWN)
765 765 keymap.register("pageup", curses.KEY_PPAGE)
766 766 keymap.register("pagedown", curses.KEY_NPAGE)
767 767 keymap.register("left", curses.KEY_LEFT)
768 768 keymap.register("right", curses.KEY_RIGHT)
769 769 keymap.register("home", curses.KEY_HOME, "\x01")
770 770 keymap.register("end", curses.KEY_END, "\x05")
771 771 keymap.register("prevattr", "<\x1b")
772 772 keymap.register("nextattr", ">\t")
773 773 keymap.register("pick", "p")
774 774 keymap.register("pickattr", "P")
775 775 keymap.register("pickallattrs", "C")
776 776 keymap.register("pickmarked", "m")
777 777 keymap.register("pickmarkedattr", "M")
778 778 keymap.register("pickinput", "i")
779 779 keymap.register("pickinputattr", "I")
780 780 keymap.register("hideattr", "h")
781 781 keymap.register("unhideattrs", "H")
782 782 keymap.register("help", "?")
783 783 keymap.register("enter", "\r\n")
784 784 keymap.register("enterattr", "E")
785 785 # FIXME: What's happening here?
786 786 keymap.register("leave", curses.KEY_BACKSPACE, "x\x08\x7f")
787 787 keymap.register("detail", "d")
788 788 keymap.register("detailattr", "D")
789 789 keymap.register("tooglemark", " ")
790 790 keymap.register("markrange", "%")
791 791 keymap.register("sortattrasc", "v")
792 792 keymap.register("sortattrdesc", "V")
793 793 keymap.register("goto", "g")
794 794 keymap.register("find", "f")
795 795 keymap.register("findbackwards", "b")
796 796 keymap.register("refresh", "r")
797 797 keymap.register("refreshfind", "R")
798 798
799 799 def __init__(self, input=None, *attrs):
800 800 """
801 801 Create a new browser. If ``attrs`` is not empty, it is the list
802 802 of attributes that will be displayed in the browser, otherwise
803 803 these will be determined by the objects on screen.
804 804 """
805 805 ipipe.Display.__init__(self, input)
806 806
807 807 self.attrs = attrs
808 808
809 809 # Stack of browser levels
810 810 self.levels = []
811 811 # how many colums to scroll (Changes when accelerating)
812 812 self.stepx = 1.
813 813
814 814 # how many rows to scroll (Changes when accelerating)
815 815 self.stepy = 1.
816 816
817 817 # Beep on the edges of the data area? (Will be set to ``False``
818 818 # once the cursor hits the edge of the screen, so we don't get
819 819 # multiple beeps).
820 820 self._dobeep = True
821 821
822 822 # Cache for registered ``curses`` colors and styles.
823 823 self._styles = {}
824 824 self._colors = {}
825 825 self._maxcolor = 1
826 826
827 827 # How many header lines do we want to paint (the numbers of levels
828 828 # we have, but with an upper bound)
829 829 self._headerlines = 1
830 830
831 831 # Index of first header line
832 832 self._firstheaderline = 0
833 833
834 834 # curses window
835 835 self.scr = None
836 836 # report in the footer line (error, executed command etc.)
837 837 self._report = None
838 838
839 839 # value to be returned to the caller (set by commands)
840 840 self.returnvalue = None
841 841
842 842 # The mode the browser is in
843 843 # e.g. normal browsing or entering an argument for a command
844 844 self.mode = "default"
845 845
846 846 # set by the SIGWINCH signal handler
847 847 self.resized = False
848 848
849 849 def nextstepx(self, step):
850 850 """
851 851 Accelerate horizontally.
852 852 """
853 853 return max(1., min(step*self.acceleratex,
854 854 self.maxspeedx*self.levels[-1].mainsizex))
855 855
856 856 def nextstepy(self, step):
857 857 """
858 858 Accelerate vertically.
859 859 """
860 860 return max(1., min(step*self.acceleratey,
861 861 self.maxspeedy*self.levels[-1].mainsizey))
862 862
863 863 def getstyle(self, style):
864 864 """
865 865 Register the ``style`` with ``curses`` or get it from the cache,
866 866 if it has been registered before.
867 867 """
868 868 try:
869 869 return self._styles[style.fg, style.bg, style.attrs]
870 870 except KeyError:
871 871 attrs = 0
872 872 for b in astyle.A2CURSES:
873 873 if style.attrs & b:
874 874 attrs |= astyle.A2CURSES[b]
875 875 try:
876 876 color = self._colors[style.fg, style.bg]
877 877 except KeyError:
878 878 curses.init_pair(
879 879 self._maxcolor,
880 880 astyle.COLOR2CURSES[style.fg],
881 881 astyle.COLOR2CURSES[style.bg]
882 882 )
883 883 color = curses.color_pair(self._maxcolor)
884 884 self._colors[style.fg, style.bg] = color
885 885 self._maxcolor += 1
886 886 c = color | attrs
887 887 self._styles[style.fg, style.bg, style.attrs] = c
888 888 return c
889 889
890 890 def addstr(self, y, x, begx, endx, text, style):
891 891 """
892 892 A version of ``curses.addstr()`` that can handle ``x`` coordinates
893 893 that are outside the screen.
894 894 """
895 895 text2 = text[max(0, begx-x):max(0, endx-x)]
896 896 if text2:
897 897 self.scr.addstr(y, max(x, begx), text2, self.getstyle(style))
898 898 return len(text)
899 899
900 900 def addchr(self, y, x, begx, endx, c, l, style):
901 901 x0 = max(x, begx)
902 902 x1 = min(x+l, endx)
903 903 if x1>x0:
904 904 self.scr.addstr(y, x0, c*(x1-x0), self.getstyle(style))
905 905 return l
906 906
907 907 def _calcheaderlines(self, levels):
908 908 # Calculate how many headerlines do we have to display, if we have
909 909 # ``levels`` browser levels
910 910 if levels is None:
911 911 levels = len(self.levels)
912 912 self._headerlines = min(self.maxheaders, levels)
913 913 self._firstheaderline = levels-self._headerlines
914 914
915 915 def getstylehere(self, style):
916 916 """
917 917 Return a style for displaying the original style ``style``
918 918 in the row the cursor is on.
919 919 """
920 920 return astyle.Style(style.fg, astyle.COLOR_BLUE, style.attrs | astyle.A_BOLD)
921 921
922 922 def report(self, msg):
923 923 """
924 924 Store the message ``msg`` for display below the footer line. This
925 925 will be displayed as soon as the screen is redrawn.
926 926 """
927 927 self._report = msg
928 928
929 929 def enter(self, item, *attrs):
930 930 """
931 931 Enter the object ``item``. If ``attrs`` is specified, it will be used
932 932 as a fixed list of attributes to display.
933 933 """
934 934 if self.levels and item is self.levels[-1].input:
935 935 curses.beep()
936 936 self.report(CommandError("Recursion on input object"))
937 937 else:
938 938 oldlevels = len(self.levels)
939 939 self._calcheaderlines(oldlevels+1)
940 940 try:
941 941 level = _BrowserLevel(
942 942 self,
943 943 item,
944 944 self.scrsizey-1-self._headerlines-2,
945 945 *attrs
946 946 )
947 947 except (KeyboardInterrupt, SystemExit):
948 948 raise
949 949 except Exception, exc:
950 950 if not self.levels:
951 951 raise
952 952 self._calcheaderlines(oldlevels)
953 953 curses.beep()
954 954 self.report(exc)
955 955 else:
956 956 self.levels.append(level)
957 957
958 958 def startkeyboardinput(self, mode):
959 959 """
960 960 Enter mode ``mode``, which requires keyboard input.
961 961 """
962 962 self.mode = mode
963 963 self.prompts[mode].start()
964 964
965 965 def keylabel(self, keycode):
966 966 """
967 967 Return a pretty name for the ``curses`` key ``keycode`` (used in the
968 968 help screen and in reports about unassigned keys).
969 969 """
970 970 if keycode <= 0xff:
971 971 specialsnames = {
972 972 ord("\n"): "RETURN",
973 973 ord(" "): "SPACE",
974 974 ord("\t"): "TAB",
975 975 ord("\x7f"): "DELETE",
976 976 ord("\x08"): "BACKSPACE",
977 977 }
978 978 if keycode in specialsnames:
979 979 return specialsnames[keycode]
980 980 elif 0x00 < keycode < 0x20:
981 981 return "CTRL-%s" % chr(keycode + 64)
982 982 return repr(chr(keycode))
983 983 for name in dir(curses):
984 984 if name.startswith("KEY_") and getattr(curses, name) == keycode:
985 985 return name
986 986 return str(keycode)
987 987
988 988 def beep(self, force=False):
989 989 if force or self._dobeep:
990 990 curses.beep()
991 991 # don't beep again (as long as the same key is pressed)
992 992 self._dobeep = False
993 993
994 994 def cmd_up(self):
995 995 """
996 996 Move the cursor to the previous row.
997 997 """
998 998 level = self.levels[-1]
999 999 self.report("up")
1000 1000 level.moveto(level.curx, level.cury-self.stepy)
1001 1001
1002 1002 def cmd_down(self):
1003 1003 """
1004 1004 Move the cursor to the next row.
1005 1005 """
1006 1006 level = self.levels[-1]
1007 1007 self.report("down")
1008 1008 level.moveto(level.curx, level.cury+self.stepy)
1009 1009
1010 1010 def cmd_pageup(self):
1011 1011 """
1012 1012 Move the cursor up one page.
1013 1013 """
1014 1014 level = self.levels[-1]
1015 1015 self.report("page up")
1016 1016 level.moveto(level.curx, level.cury-level.mainsizey+self.pageoverlapy)
1017 1017
1018 1018 def cmd_pagedown(self):
1019 1019 """
1020 1020 Move the cursor down one page.
1021 1021 """
1022 1022 level = self.levels[-1]
1023 1023 self.report("page down")
1024 1024 level.moveto(level.curx, level.cury+level.mainsizey-self.pageoverlapy)
1025 1025
1026 1026 def cmd_left(self):
1027 1027 """
1028 1028 Move the cursor left.
1029 1029 """
1030 1030 level = self.levels[-1]
1031 1031 self.report("left")
1032 1032 level.moveto(level.curx-self.stepx, level.cury)
1033 1033
1034 1034 def cmd_right(self):
1035 1035 """
1036 1036 Move the cursor right.
1037 1037 """
1038 1038 level = self.levels[-1]
1039 1039 self.report("right")
1040 1040 level.moveto(level.curx+self.stepx, level.cury)
1041 1041
1042 1042 def cmd_home(self):
1043 1043 """
1044 1044 Move the cursor to the first column.
1045 1045 """
1046 1046 level = self.levels[-1]
1047 1047 self.report("home")
1048 1048 level.moveto(0, level.cury)
1049 1049
1050 1050 def cmd_end(self):
1051 1051 """
1052 1052 Move the cursor to the last column.
1053 1053 """
1054 1054 level = self.levels[-1]
1055 1055 self.report("end")
1056 1056 level.moveto(level.datasizex+level.mainsizey-self.pageoverlapx, level.cury)
1057 1057
1058 1058 def cmd_prevattr(self):
1059 1059 """
1060 1060 Move the cursor one attribute column to the left.
1061 1061 """
1062 1062 level = self.levels[-1]
1063 1063 if level.displayattr[0] is None or level.displayattr[0] == 0:
1064 1064 self.beep()
1065 1065 else:
1066 1066 self.report("prevattr")
1067 1067 pos = 0
1068 1068 for (i, attrname) in enumerate(level.displayattrs):
1069 1069 if i == level.displayattr[0]-1:
1070 1070 break
1071 1071 pos += level.colwidths[attrname] + 1
1072 1072 level.moveto(pos, level.cury)
1073 1073
1074 1074 def cmd_nextattr(self):
1075 1075 """
1076 1076 Move the cursor one attribute column to the right.
1077 1077 """
1078 1078 level = self.levels[-1]
1079 1079 if level.displayattr[0] is None or level.displayattr[0] == len(level.displayattrs)-1:
1080 1080 self.beep()
1081 1081 else:
1082 1082 self.report("nextattr")
1083 1083 pos = 0
1084 1084 for (i, attrname) in enumerate(level.displayattrs):
1085 1085 if i == level.displayattr[0]+1:
1086 1086 break
1087 1087 pos += level.colwidths[attrname] + 1
1088 1088 level.moveto(pos, level.cury)
1089 1089
1090 1090 def cmd_pick(self):
1091 1091 """
1092 1092 'Pick' the object under the cursor (i.e. the row the cursor is on).
1093 1093 This leaves the browser and returns the picked object to the caller.
1094 1094 (In IPython this object will be available as the ``_`` variable.)
1095 1095 """
1096 1096 level = self.levels[-1]
1097 1097 self.returnvalue = level.items[level.cury].item
1098 1098 return True
1099 1099
1100 1100 def cmd_pickattr(self):
1101 1101 """
1102 1102 'Pick' the attribute under the cursor (i.e. the row/column the
1103 1103 cursor is on).
1104 1104 """
1105 1105 level = self.levels[-1]
1106 1106 attr = level.displayattr[1]
1107 1107 if attr is ipipe.noitem:
1108 1108 curses.beep()
1109 1109 self.report(CommandError("no column under cursor"))
1110 1110 return
1111 1111 value = attr.value(level.items[level.cury].item)
1112 1112 if value is ipipe.noitem:
1113 1113 curses.beep()
1114 1114 self.report(AttributeError(attr.name()))
1115 1115 else:
1116 1116 self.returnvalue = value
1117 1117 return True
1118 1118
1119 1119 def cmd_pickallattrs(self):
1120 1120 """
1121 1121 Pick' the complete column under the cursor (i.e. the attribute under
1122 1122 the cursor) from all currently fetched objects. These attributes
1123 1123 will be returned as a list.
1124 1124 """
1125 1125 level = self.levels[-1]
1126 1126 attr = level.displayattr[1]
1127 1127 if attr is ipipe.noitem:
1128 1128 curses.beep()
1129 1129 self.report(CommandError("no column under cursor"))
1130 1130 return
1131 1131 result = []
1132 1132 for cache in level.items:
1133 1133 value = attr.value(cache.item)
1134 1134 if value is not ipipe.noitem:
1135 1135 result.append(value)
1136 1136 self.returnvalue = result
1137 1137 return True
1138 1138
1139 1139 def cmd_pickmarked(self):
1140 1140 """
1141 1141 'Pick' marked objects. Marked objects will be returned as a list.
1142 1142 """
1143 1143 level = self.levels[-1]
1144 1144 self.returnvalue = [cache.item for cache in level.items if cache.marked]
1145 1145 return True
1146 1146
1147 1147 def cmd_pickmarkedattr(self):
1148 1148 """
1149 1149 'Pick' the attribute under the cursor from all marked objects
1150 1150 (This returns a list).
1151 1151 """
1152 1152
1153 1153 level = self.levels[-1]
1154 1154 attr = level.displayattr[1]
1155 1155 if attr is ipipe.noitem:
1156 1156 curses.beep()
1157 1157 self.report(CommandError("no column under cursor"))
1158 1158 return
1159 1159 result = []
1160 1160 for cache in level.items:
1161 1161 if cache.marked:
1162 1162 value = attr.value(cache.item)
1163 1163 if value is not ipipe.noitem:
1164 1164 result.append(value)
1165 1165 self.returnvalue = result
1166 1166 return True
1167 1167
1168 1168 def cmd_pickinput(self):
1169 1169 """
1170 1170 Use the object under the cursor (i.e. the row the cursor is on) as
1171 1171 the next input line. This leaves the browser and puts the picked object
1172 1172 in the input.
1173 1173 """
1174 1174 level = self.levels[-1]
1175 1175 value = level.items[level.cury].item
1176 1176 self.returnvalue = None
1177 1177 api = ipapi.get()
1178 1178 api.set_next_input(str(value))
1179 1179 return True
1180 1180
1181 1181 def cmd_pickinputattr(self):
1182 1182 """
1183 1183 Use the attribute under the cursor i.e. the row/column the cursor is on)
1184 1184 as the next input line. This leaves the browser and puts the picked
1185 1185 object in the input.
1186 1186 """
1187 1187 level = self.levels[-1]
1188 1188 attr = level.displayattr[1]
1189 1189 if attr is ipipe.noitem:
1190 1190 curses.beep()
1191 1191 self.report(CommandError("no column under cursor"))
1192 1192 return
1193 1193 value = attr.value(level.items[level.cury].item)
1194 1194 if value is ipipe.noitem:
1195 1195 curses.beep()
1196 1196 self.report(AttributeError(attr.name()))
1197 1197 self.returnvalue = None
1198 1198 api = ipapi.get()
1199 1199 api.set_next_input(str(value))
1200 1200 return True
1201 1201
1202 1202 def cmd_markrange(self):
1203 1203 """
1204 1204 Mark all objects from the last marked object before the current cursor
1205 1205 position to the cursor position.
1206 1206 """
1207 1207 level = self.levels[-1]
1208 1208 self.report("markrange")
1209 1209 start = None
1210 1210 if level.items:
1211 1211 for i in xrange(level.cury, -1, -1):
1212 1212 if level.items[i].marked:
1213 1213 start = i
1214 1214 break
1215 1215 if start is None:
1216 1216 self.report(CommandError("no mark before cursor"))
1217 1217 curses.beep()
1218 1218 else:
1219 1219 for i in xrange(start, level.cury+1):
1220 1220 cache = level.items[i]
1221 1221 if not cache.marked:
1222 1222 cache.marked = True
1223 1223 level.marked += 1
1224 1224
1225 1225 def cmd_enter(self):
1226 1226 """
1227 1227 Enter the object under the cursor. (what this mean depends on the object
1228 1228 itself (i.e. how it implements iteration). This opens a new browser 'level'.
1229 1229 """
1230 1230 level = self.levels[-1]
1231 1231 try:
1232 1232 item = level.items[level.cury].item
1233 1233 except IndexError:
1234 1234 self.report(CommandError("No object"))
1235 1235 curses.beep()
1236 1236 else:
1237 1237 self.report("entering object...")
1238 1238 self.enter(item)
1239 1239
1240 1240 def cmd_leave(self):
1241 1241 """
1242 1242 Leave the current browser level and go back to the previous one.
1243 1243 """
1244 1244 self.report("leave")
1245 1245 if len(self.levels) > 1:
1246 1246 self._calcheaderlines(len(self.levels)-1)
1247 1247 self.levels.pop(-1)
1248 1248 else:
1249 1249 self.report(CommandError("This is the last level"))
1250 1250 curses.beep()
1251 1251
1252 1252 def cmd_enterattr(self):
1253 1253 """
1254 1254 Enter the attribute under the cursor.
1255 1255 """
1256 1256 level = self.levels[-1]
1257 1257 attr = level.displayattr[1]
1258 1258 if attr is ipipe.noitem:
1259 1259 curses.beep()
1260 1260 self.report(CommandError("no column under cursor"))
1261 1261 return
1262 1262 try:
1263 1263 item = level.items[level.cury].item
1264 1264 except IndexError:
1265 1265 self.report(CommandError("No object"))
1266 1266 curses.beep()
1267 1267 else:
1268 1268 value = attr.value(item)
1269 1269 name = attr.name()
1270 1270 if value is ipipe.noitem:
1271 1271 self.report(AttributeError(name))
1272 1272 else:
1273 1273 self.report("entering object attribute %s..." % name)
1274 1274 self.enter(value)
1275 1275
1276 1276 def cmd_detail(self):
1277 1277 """
1278 1278 Show a detail view of the object under the cursor. This shows the
1279 1279 name, type, doc string and value of the object attributes (and it
1280 1280 might show more attributes than in the list view, depending on
1281 1281 the object).
1282 1282 """
1283 1283 level = self.levels[-1]
1284 1284 try:
1285 1285 item = level.items[level.cury].item
1286 1286 except IndexError:
1287 1287 self.report(CommandError("No object"))
1288 1288 curses.beep()
1289 1289 else:
1290 1290 self.report("entering detail view for object...")
1291 1291 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
1292 1292 self.enter(attrs)
1293 1293
1294 1294 def cmd_detailattr(self):
1295 1295 """
1296 1296 Show a detail view of the attribute under the cursor.
1297 1297 """
1298 1298 level = self.levels[-1]
1299 1299 attr = level.displayattr[1]
1300 1300 if attr is ipipe.noitem:
1301 1301 curses.beep()
1302 1302 self.report(CommandError("no attribute"))
1303 1303 return
1304 1304 try:
1305 1305 item = level.items[level.cury].item
1306 1306 except IndexError:
1307 1307 self.report(CommandError("No object"))
1308 1308 curses.beep()
1309 1309 else:
1310 1310 try:
1311 1311 item = attr.value(item)
1312 1312 except (KeyboardInterrupt, SystemExit):
1313 1313 raise
1314 1314 except Exception, exc:
1315 1315 self.report(exc)
1316 1316 else:
1317 1317 self.report("entering detail view for attribute %s..." % attr.name())
1318 1318 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
1319 1319 self.enter(attrs)
1320 1320
1321 1321 def cmd_tooglemark(self):
1322 1322 """
1323 1323 Mark/unmark the object under the cursor. Marked objects have a '!'
1324 1324 after the row number).
1325 1325 """
1326 1326 level = self.levels[-1]
1327 1327 self.report("toggle mark")
1328 1328 try:
1329 1329 item = level.items[level.cury]
1330 1330 except IndexError: # no items?
1331 1331 pass
1332 1332 else:
1333 1333 if item.marked:
1334 1334 item.marked = False
1335 1335 level.marked -= 1
1336 1336 else:
1337 1337 item.marked = True
1338 1338 level.marked += 1
1339 1339
1340 1340 def cmd_sortattrasc(self):
1341 1341 """
1342 1342 Sort the objects (in ascending order) using the attribute under
1343 1343 the cursor as the sort key.
1344 1344 """
1345 1345 level = self.levels[-1]
1346 1346 attr = level.displayattr[1]
1347 1347 if attr is ipipe.noitem:
1348 1348 curses.beep()
1349 1349 self.report(CommandError("no column under cursor"))
1350 1350 return
1351 1351 self.report("sort by %s (ascending)" % attr.name())
1352 1352 def key(item):
1353 1353 try:
1354 1354 return attr.value(item)
1355 1355 except (KeyboardInterrupt, SystemExit):
1356 1356 raise
1357 1357 except Exception:
1358 1358 return None
1359 1359 level.sort(key)
1360 1360
1361 1361 def cmd_sortattrdesc(self):
1362 1362 """
1363 1363 Sort the objects (in descending order) using the attribute under
1364 1364 the cursor as the sort key.
1365 1365 """
1366 1366 level = self.levels[-1]
1367 1367 attr = level.displayattr[1]
1368 1368 if attr is ipipe.noitem:
1369 1369 curses.beep()
1370 1370 self.report(CommandError("no column under cursor"))
1371 1371 return
1372 1372 self.report("sort by %s (descending)" % attr.name())
1373 1373 def key(item):
1374 1374 try:
1375 1375 return attr.value(item)
1376 1376 except (KeyboardInterrupt, SystemExit):
1377 1377 raise
1378 1378 except Exception:
1379 1379 return None
1380 1380 level.sort(key, reverse=True)
1381 1381
1382 1382 def cmd_hideattr(self):
1383 1383 """
1384 1384 Hide the attribute under the cursor.
1385 1385 """
1386 1386 level = self.levels[-1]
1387 1387 if level.displayattr[0] is None:
1388 1388 self.beep()
1389 1389 else:
1390 1390 self.report("hideattr")
1391 1391 level.hiddenattrs.add(level.displayattr[1])
1392 1392 level.moveto(level.curx, level.cury, refresh=True)
1393 1393
1394 1394 def cmd_unhideattrs(self):
1395 1395 """
1396 1396 Make all attributes visible again.
1397 1397 """
1398 1398 level = self.levels[-1]
1399 1399 self.report("unhideattrs")
1400 1400 level.hiddenattrs.clear()
1401 1401 level.moveto(level.curx, level.cury, refresh=True)
1402 1402
1403 1403 def cmd_goto(self):
1404 1404 """
1405 1405 Jump to a row. The row number can be entered at the
1406 1406 bottom of the screen.
1407 1407 """
1408 1408 self.startkeyboardinput("goto")
1409 1409
1410 1410 def cmd_find(self):
1411 1411 """
1412 1412 Search forward for a row. The search condition can be entered at the
1413 1413 bottom of the screen.
1414 1414 """
1415 1415 self.startkeyboardinput("find")
1416 1416
1417 1417 def cmd_findbackwards(self):
1418 1418 """
1419 1419 Search backward for a row. The search condition can be entered at the
1420 1420 bottom of the screen.
1421 1421 """
1422 1422 self.startkeyboardinput("findbackwards")
1423 1423
1424 1424 def cmd_refresh(self):
1425 1425 """
1426 1426 Refreshes the display by restarting the iterator.
1427 1427 """
1428 1428 level = self.levels[-1]
1429 1429 self.report("refresh")
1430 1430 level.refresh()
1431 1431
1432 1432 def cmd_refreshfind(self):
1433 1433 """
1434 1434 Refreshes the display by restarting the iterator and goes back to the
1435 1435 same object the cursor was on before restarting (if this object can't be
1436 1436 found the cursor jumps back to the first object).
1437 1437 """
1438 1438 level = self.levels[-1]
1439 1439 self.report("refreshfind")
1440 1440 level.refreshfind()
1441 1441
1442 1442 def cmd_help(self):
1443 1443 """
1444 1444 Opens the help screen as a new browser level, describing keyboard
1445 1445 shortcuts.
1446 1446 """
1447 1447 for level in self.levels:
1448 1448 if isinstance(level.input, _BrowserHelp):
1449 1449 curses.beep()
1450 1450 self.report(CommandError("help already active"))
1451 1451 return
1452 1452
1453 1453 self.enter(_BrowserHelp(self))
1454 1454
1455 1455 def cmd_quit(self):
1456 1456 """
1457 1457 Quit the browser and return to the IPython prompt.
1458 1458 """
1459 1459 self.returnvalue = None
1460 1460 return True
1461 1461
1462 1462 def sigwinchhandler(self, signal, frame):
1463 1463 self.resized = True
1464 1464
1465 1465 def _dodisplay(self, scr):
1466 1466 """
1467 1467 This method is the workhorse of the browser. It handles screen
1468 1468 drawing and the keyboard.
1469 1469 """
1470 1470 self.scr = scr
1471 1471 curses.halfdelay(1)
1472 1472 footery = 2
1473 1473
1474 1474 keys = []
1475 1475 for cmd in ("quit", "help"):
1476 1476 key = self.keymap.findkey(cmd, None)
1477 1477 if key is not None:
1478 1478 keys.append("%s=%s" % (self.keylabel(key), cmd))
1479 1479 helpmsg = " | %s" % " ".join(keys)
1480 1480
1481 1481 scr.clear()
1482 1482 msg = "Fetching first batch of objects..."
1483 1483 (self.scrsizey, self.scrsizex) = scr.getmaxyx()
1484 1484 scr.addstr(self.scrsizey//2, (self.scrsizex-len(msg))//2, msg)
1485 1485 scr.refresh()
1486 1486
1487 1487 lastc = -1
1488 1488
1489 1489 self.levels = []
1490 1490 # enter the first level
1491 1491 self.enter(self.input, *self.attrs)
1492 1492
1493 1493 self._calcheaderlines(None)
1494 1494
1495 1495 while True:
1496 1496 level = self.levels[-1]
1497 1497 (self.scrsizey, self.scrsizex) = scr.getmaxyx()
1498 1498 level.mainsizey = self.scrsizey-1-self._headerlines-footery
1499 1499
1500 1500 # Paint object header
1501 1501 for i in xrange(self._firstheaderline, self._firstheaderline+self._headerlines):
1502 1502 lv = self.levels[i]
1503 1503 posx = 0
1504 1504 posy = i-self._firstheaderline
1505 1505 endx = self.scrsizex
1506 1506 if i: # not the first level
1507 1507 msg = " (%d/%d" % (self.levels[i-1].cury, len(self.levels[i-1].items))
1508 1508 if not self.levels[i-1].exhausted:
1509 1509 msg += "+"
1510 1510 msg += ") "
1511 1511 endx -= len(msg)+1
1512 1512 posx += self.addstr(posy, posx, 0, endx, " ibrowse #%d: " % i, self.style_objheadertext)
1513 1513 for (style, text) in lv.header:
1514 1514 posx += self.addstr(posy, posx, 0, endx, text, self.style_objheaderobject)
1515 1515 if posx >= endx:
1516 1516 break
1517 1517 if i:
1518 1518 posx += self.addstr(posy, posx, 0, self.scrsizex, msg, self.style_objheadernumber)
1519 1519 posx += self.addchr(posy, posx, 0, self.scrsizex, " ", self.scrsizex-posx, self.style_objheadernumber)
1520 1520
1521 1521 if not level.items:
1522 1522 self.addchr(self._headerlines, 0, 0, self.scrsizex, " ", self.scrsizex, self.style_colheader)
1523 1523 self.addstr(self._headerlines+1, 0, 0, self.scrsizex, " <empty>", astyle.style_error)
1524 1524 scr.clrtobot()
1525 1525 else:
1526 1526 # Paint column headers
1527 1527 scr.move(self._headerlines, 0)
1528 1528 scr.addstr(" %*s " % (level.numbersizex, "#"), self.getstyle(self.style_colheader))
1529 1529 scr.addstr(self.headersepchar, self.getstyle(self.style_colheadersep))
1530 1530 begx = level.numbersizex+3
1531 1531 posx = begx-level.datastartx
1532 1532 for attr in level.displayattrs:
1533 1533 attrname = attr.name()
1534 1534 cwidth = level.colwidths[attr]
1535 1535 header = attrname.ljust(cwidth)
1536 1536 if attr is level.displayattr[1]:
1537 1537 style = self.style_colheaderhere
1538 1538 else:
1539 1539 style = self.style_colheader
1540 1540 posx += self.addstr(self._headerlines, posx, begx, self.scrsizex, header, style)
1541 1541 posx += self.addstr(self._headerlines, posx, begx, self.scrsizex, self.headersepchar, self.style_colheadersep)
1542 1542 if posx >= self.scrsizex:
1543 1543 break
1544 1544 else:
1545 1545 scr.addstr(" "*(self.scrsizex-posx), self.getstyle(self.style_colheader))
1546 1546
1547 1547 # Paint rows
1548 1548 posy = self._headerlines+1+level.datastarty
1549 1549 for i in xrange(level.datastarty, min(level.datastarty+level.mainsizey, len(level.items))):
1550 1550 cache = level.items[i]
1551 1551 if i == level.cury:
1552 1552 style = self.style_numberhere
1553 1553 else:
1554 1554 style = self.style_number
1555 1555
1556 1556 posy = self._headerlines+1+i-level.datastarty
1557 1557 posx = begx-level.datastartx
1558 1558
1559 1559 scr.move(posy, 0)
1560 1560 scr.addstr(" %*d%s" % (level.numbersizex, i, " !"[cache.marked]), self.getstyle(style))
1561 1561 scr.addstr(self.headersepchar, self.getstyle(self.style_sep))
1562 1562
1563 1563 for attrname in level.displayattrs:
1564 1564 cwidth = level.colwidths[attrname]
1565 1565 try:
1566 1566 (align, length, parts) = level.displayrows[i-level.datastarty][attrname]
1567 1567 except KeyError:
1568 1568 align = 2
1569 1569 style = astyle.style_nodata
1570 1570 if i == level.cury:
1571 1571 style = self.getstylehere(style)
1572 1572 padstyle = self.style_datapad
1573 1573 sepstyle = self.style_sep
1574 1574 if i == level.cury:
1575 1575 padstyle = self.getstylehere(padstyle)
1576 1576 sepstyle = self.getstylehere(sepstyle)
1577 1577 if align == 2:
1578 1578 posx += self.addchr(posy, posx, begx, self.scrsizex, self.nodatachar, cwidth, style)
1579 1579 else:
1580 1580 if align == 1:
1581 1581 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle)
1582 1582 elif align == 0:
1583 1583 pad1 = (cwidth-length)//2
1584 1584 pad2 = cwidth-length-len(pad1)
1585 1585 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad1, padstyle)
1586 1586 for (style, text) in parts:
1587 1587 if i == level.cury:
1588 1588 style = self.getstylehere(style)
1589 1589 posx += self.addstr(posy, posx, begx, self.scrsizex, text, style)
1590 1590 if posx >= self.scrsizex:
1591 1591 break
1592 1592 if align == -1:
1593 1593 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, cwidth-length, padstyle)
1594 1594 elif align == 0:
1595 1595 posx += self.addchr(posy, posx, begx, self.scrsizex, self.datapadchar, pad2, padstyle)
1596 1596 posx += self.addstr(posy, posx, begx, self.scrsizex, self.datasepchar, sepstyle)
1597 1597 else:
1598 1598 scr.clrtoeol()
1599 1599
1600 1600 # Add blank row headers for the rest of the screen
1601 1601 for posy in xrange(posy+1, self.scrsizey-2):
1602 1602 scr.addstr(posy, 0, " " * (level.numbersizex+2), self.getstyle(self.style_colheader))
1603 1603 scr.clrtoeol()
1604 1604
1605 1605 posy = self.scrsizey-footery
1606 1606 # Display footer
1607 1607 scr.addstr(posy, 0, " "*self.scrsizex, self.getstyle(self.style_footer))
1608 1608
1609 1609 if level.exhausted:
1610 1610 flag = ""
1611 1611 else:
1612 1612 flag = "+"
1613 1613
1614 1614 endx = self.scrsizex-len(helpmsg)-1
1615 1615 scr.addstr(posy, endx, helpmsg, self.getstyle(self.style_footer))
1616 1616
1617 1617 posx = 0
1618 1618 msg = " %d%s objects (%d marked): " % (len(level.items), flag, level.marked)
1619 1619 posx += self.addstr(posy, posx, 0, endx, msg, self.style_footer)
1620 1620 try:
1621 1621 item = level.items[level.cury].item
1622 1622 except IndexError: # empty
1623 1623 pass
1624 1624 else:
1625 1625 for (nostyle, text) in ipipe.xrepr(item, "footer"):
1626 1626 if not isinstance(nostyle, int):
1627 1627 posx += self.addstr(posy, posx, 0, endx, text, self.style_footer)
1628 1628 if posx >= endx:
1629 1629 break
1630 1630
1631 1631 attrstyle = [(astyle.style_default, "no attribute")]
1632 1632 attr = level.displayattr[1]
1633 1633 if attr is not ipipe.noitem and not isinstance(attr, ipipe.SelfDescriptor):
1634 1634 posx += self.addstr(posy, posx, 0, endx, " | ", self.style_footer)
1635 1635 posx += self.addstr(posy, posx, 0, endx, attr.name(), self.style_footer)
1636 1636 posx += self.addstr(posy, posx, 0, endx, ": ", self.style_footer)
1637 1637 try:
1638 1638 value = attr.value(item)
1639 1639 except (SystemExit, KeyboardInterrupt):
1640 1640 raise
1641 1641 except Exception, exc:
1642 1642 value = exc
1643 1643 if value is not ipipe.noitem:
1644 1644 attrstyle = ipipe.xrepr(value, "footer")
1645 1645 for (nostyle, text) in attrstyle:
1646 1646 if not isinstance(nostyle, int):
1647 1647 posx += self.addstr(posy, posx, 0, endx, text, self.style_footer)
1648 1648 if posx >= endx:
1649 1649 break
1650 1650
1651 1651 try:
1652 1652 # Display input prompt
1653 1653 if self.mode in self.prompts:
1654 1654 history = self.prompts[self.mode]
1655 1655 posx = 0
1656 1656 posy = self.scrsizey-1
1657 1657 posx += self.addstr(posy, posx, 0, endx, history.prompt, astyle.style_default)
1658 1658 posx += self.addstr(posy, posx, 0, endx, " [", astyle.style_default)
1659 1659 if history.cury==-1:
1660 1660 text = "new"
1661 1661 else:
1662 1662 text = str(history.cury+1)
1663 1663 posx += self.addstr(posy, posx, 0, endx, text, astyle.style_type_number)
1664 1664 if history.history:
1665 1665 posx += self.addstr(posy, posx, 0, endx, "/", astyle.style_default)
1666 1666 posx += self.addstr(posy, posx, 0, endx, str(len(history.history)), astyle.style_type_number)
1667 1667 posx += self.addstr(posy, posx, 0, endx, "]: ", astyle.style_default)
1668 1668 inputstartx = posx
1669 1669 posx += self.addstr(posy, posx, 0, endx, history.input, astyle.style_default)
1670 1670 # Display report
1671 1671 else:
1672 1672 if self._report is not None:
1673 1673 if isinstance(self._report, Exception):
1674 1674 style = self.getstyle(astyle.style_error)
1675 1675 if self._report.__class__.__module__ == "exceptions":
1676 1676 msg = "%s: %s" % \
1677 1677 (self._report.__class__.__name__, self._report)
1678 1678 else:
1679 1679 msg = "%s.%s: %s" % \
1680 1680 (self._report.__class__.__module__,
1681 1681 self._report.__class__.__name__, self._report)
1682 1682 else:
1683 1683 style = self.getstyle(self.style_report)
1684 1684 msg = self._report
1685 1685 scr.addstr(self.scrsizey-1, 0, msg[:self.scrsizex], style)
1686 1686 self._report = None
1687 1687 else:
1688 1688 scr.move(self.scrsizey-1, 0)
1689 1689 except curses.error:
1690 1690 # Protect against errors from writing to the last line
1691 1691 pass
1692 1692 scr.clrtoeol()
1693 1693
1694 1694 # Position cursor
1695 1695 if self.mode in self.prompts:
1696 1696 history = self.prompts[self.mode]
1697 1697 scr.move(self.scrsizey-1, inputstartx+history.curx)
1698 1698 else:
1699 1699 scr.move(
1700 1700 1+self._headerlines+level.cury-level.datastarty,
1701 1701 level.numbersizex+3+level.curx-level.datastartx
1702 1702 )
1703 1703 scr.refresh()
1704 1704
1705 1705 # Check keyboard
1706 1706 while True:
1707 1707 c = scr.getch()
1708 1708 if self.resized:
1709 1709 size = fcntl.ioctl(0, tty.TIOCGWINSZ, "12345678")
1710 1710 size = struct.unpack("4H", size)
1711 1711 oldsize = scr.getmaxyx()
1712 1712 scr.erase()
1713 1713 curses.resize_term(size[0], size[1])
1714 1714 newsize = scr.getmaxyx()
1715 1715 scr.erase()
1716 1716 for l in self.levels:
1717 1717 l.mainsizey += newsize[0]-oldsize[0]
1718 1718 l.moveto(l.curx, l.cury, refresh=True)
1719 1719 scr.refresh()
1720 1720 self.resized = False
1721 1721 break # Redisplay
1722 1722 if self.mode in self.prompts:
1723 1723 if self.prompts[self.mode].handlekey(self, c):
1724 1724 break # Redisplay
1725 1725 else:
1726 1726 # if no key is pressed slow down and beep again
1727 1727 if c == -1:
1728 1728 self.stepx = 1.
1729 1729 self.stepy = 1.
1730 1730 self._dobeep = True
1731 1731 else:
1732 1732 # if a different key was pressed slow down and beep too
1733 1733 if c != lastc:
1734 1734 lastc = c
1735 1735 self.stepx = 1.
1736 1736 self.stepy = 1.
1737 1737 self._dobeep = True
1738 1738 cmdname = self.keymap.get(c, None)
1739 1739 if cmdname is None:
1740 1740 self.report(
1741 1741 UnassignedKeyError("Unassigned key %s" %
1742 1742 self.keylabel(c)))
1743 1743 else:
1744 1744 cmdfunc = getattr(self, "cmd_%s" % cmdname, None)
1745 1745 if cmdfunc is None:
1746 1746 self.report(
1747 1747 UnknownCommandError("Unknown command %r" %
1748 1748 (cmdname,)))
1749 1749 elif cmdfunc():
1750 1750 returnvalue = self.returnvalue
1751 1751 self.returnvalue = None
1752 1752 return returnvalue
1753 1753 self.stepx = self.nextstepx(self.stepx)
1754 1754 self.stepy = self.nextstepy(self.stepy)
1755 1755 curses.flushinp() # get rid of type ahead
1756 1756 break # Redisplay
1757 1757 self.scr = None
1758 1758
1759 1759 def display(self):
1760 1760 if hasattr(curses, "resize_term"):
1761 1761 oldhandler = signal.signal(signal.SIGWINCH, self.sigwinchhandler)
1762 1762 try:
1763 1763 return curses.wrapper(self._dodisplay)
1764 1764 finally:
1765 1765 signal.signal(signal.SIGWINCH, oldhandler)
1766 1766 else:
1767 1767 return curses.wrapper(self._dodisplay)
@@ -1,1126 +1,1126 b''
1 1 # -*- coding: iso-8859-1 -*-
2 2
3 3 import ipipe, os, webbrowser, urllib
4 from IPython import ipapi
4 from IPython.core import ipapi
5 5 import wx
6 6 import wx.grid, wx.html
7 7
8 8 try:
9 9 sorted
10 10 except NameError:
11 11 from ipipe import sorted
12 12 try:
13 13 set
14 14 except:
15 15 from sets import Set as set
16 16
17 17
18 18 __all__ = ["igrid"]
19 19
20 20
21 21 help = """
22 22 <?xml version='1.0' encoding='iso-8859-1'?>
23 23 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
24 24 <html>
25 25 <head>
26 26 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
27 27 <link rel="stylesheet" href="igrid_help.css" type="text/css" />
28 28 <title>igrid help</title>
29 29 </head>
30 30 <body>
31 31 <h1>igrid help</h1>
32 32
33 33
34 34 <h2>Commands</h2>
35 35
36 36
37 37 <h3>pick (P)</h3>
38 38 <p>Pick the whole row (object is available as "_")</p>
39 39
40 40 <h3>pickattr (Shift-P)</h3>
41 41 <p>Pick the attribute under the cursor</p>
42 42
43 43 <h3>pickallattrs (Shift-C)</h3>
44 44 <p>Pick the complete column under the cursor (i.e. the attribute under the
45 45 cursor) from all currently fetched objects. These attributes will be returned
46 46 as a list.</p>
47 47
48 48 <h3>pickinput (I)</h3>
49 49 <p>Pick the current row as next input line in IPython. Additionally the row is stored as "_"</p>
50 50
51 51 <h3>pickinputattr (Shift-I)</h3>
52 52 <p>Pick the attribute under the cursor as next input line in IPython. Additionally the row is stored as "_"</p>
53 53
54 54 <h3>enter (E)</h3>
55 55 <p>Enter the object under the cursor. (what this mean depends on the object
56 56 itself, i.e. how it implements iteration). This opens a new browser 'level'.</p>
57 57
58 58 <h3>enterattr (Shift-E)</h3>
59 59 <p>Enter the attribute under the cursor.</p>
60 60
61 61 <h3>detail (D)</h3>
62 62 <p>Show a detail view of the object under the cursor. This shows the name,
63 63 type, doc string and value of the object attributes (and it might show more
64 64 attributes than in the list view, depending on the object).</p>
65 65
66 66 <h3>detailattr (Shift-D)</h3>
67 67 <p>Show a detail view of the attribute under the cursor.</p>
68 68
69 69 <h3>pickrows (M)</h3>
70 70 <p>Pick multiple selected rows (M)</p>
71 71
72 72 <h3>pickrowsattr (CTRL-M)</h3>
73 73 <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p>
74 74
75 75 <h3>find (CTRL-F)</h3>
76 76 <p>Find text</p>
77 77
78 78 <h3>find_expression (CTRL-Shift-F)</h3>
79 79 <p>Find entries matching an expression</p>
80 80
81 81 <h3>find_next (F3)</h3>
82 82 <p>Find next occurrence</p>
83 83
84 84 <h3>find_previous (Shift-F3)</h3>
85 85 <p>Find previous occurrence</p>
86 86
87 87 <h3>sortattrasc (V)</h3>
88 88 <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p>
89 89
90 90 <h3>sortattrdesc (Shift-V)</h3>
91 91 <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p>
92 92
93 93 <h3>refresh_once (R, F5)</h3>
94 94 <p>Refreshes the display by restarting the iterator</p>
95 95
96 96 <h3>refresh_every_second</h3>
97 97 <p>Refreshes the display by restarting the iterator every second until stopped by stop_refresh.</p>
98 98
99 99 <h3>refresh_interval</h3>
100 100 <p>Refreshes the display by restarting the iterator every X ms (X is a custom interval set by the user) until stopped by stop_refresh.</p>
101 101
102 102 <h3>stop_refresh</h3>
103 103 <p>Stops all refresh timers.</p>
104 104
105 105 <h3>leave (Backspace, DEL, X)</h3>
106 106 <p>Close current tab (and all the tabs to the right of the current one).</h3>
107 107
108 108 <h3>quit (ESC,Q)</h3>
109 109 <p>Quit igrid and return to the IPython prompt.</p>
110 110
111 111
112 112 <h2>Navigation</h2>
113 113
114 114
115 115 <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3>
116 116
117 117 <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3>
118 118
119 119 <h3>Move the cursor one column to the left (&lt;)</h3>
120 120
121 121 <h3>Move the cursor one column to the right (&gt;)</h3>
122 122
123 123 <h3>Jump to the first row in the current column (CTRL-Up)</h3>
124 124
125 125 <h3>Jump to the last row in the current column (CTRL-Down)</h3>
126 126
127 127 </body>
128 128 </html>
129 129
130 130 """
131 131
132 132
133 133 class IGridRenderer(wx.grid.PyGridCellRenderer):
134 134 """
135 135 This is a custom renderer for our IGridGrid
136 136 """
137 137 def __init__(self, table):
138 138 self.maxchars = 200
139 139 self.table = table
140 140 self.colormap = (
141 141 ( 0, 0, 0),
142 142 (174, 0, 0),
143 143 ( 0, 174, 0),
144 144 (174, 174, 0),
145 145 ( 0, 0, 174),
146 146 (174, 0, 174),
147 147 ( 0, 174, 174),
148 148 ( 64, 64, 64)
149 149 )
150 150
151 151 wx.grid.PyGridCellRenderer.__init__(self)
152 152
153 153 def _getvalue(self, row, col):
154 154 try:
155 155 value = self.table._displayattrs[col].value(self.table.items[row])
156 156 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
157 157 except Exception, exc:
158 158 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
159 159 return (align, text)
160 160
161 161 def GetBestSize(self, grid, attr, dc, row, col):
162 162 text = grid.GetCellValue(row, col)
163 163 (align, text) = self._getvalue(row, col)
164 164 dc.SetFont(attr.GetFont())
165 165 (w, h) = dc.GetTextExtent(str(text))
166 166 return wx.Size(min(w+2, 600), h+2) # add border
167 167
168 168 def Draw(self, grid, attr, dc, rect, row, col, isSelected):
169 169 """
170 170 Takes care of drawing everything in the cell; aligns the text
171 171 """
172 172 text = grid.GetCellValue(row, col)
173 173 (align, text) = self._getvalue(row, col)
174 174 if isSelected:
175 175 bg = grid.GetSelectionBackground()
176 176 else:
177 177 bg = ["white", (240, 240, 240)][row%2]
178 178 dc.SetTextBackground(bg)
179 179 dc.SetBrush(wx.Brush(bg, wx.SOLID))
180 180 dc.SetPen(wx.TRANSPARENT_PEN)
181 181 dc.SetFont(attr.GetFont())
182 182 dc.DrawRectangleRect(rect)
183 183 dc.SetClippingRect(rect)
184 184 # Format the text
185 185 if align == -1: # left alignment
186 186 (width, height) = dc.GetTextExtent(str(text))
187 187 x = rect[0]+1
188 188 y = rect[1]+0.5*(rect[3]-height)
189 189
190 190 for (style, part) in text:
191 191 if isSelected:
192 192 fg = grid.GetSelectionForeground()
193 193 else:
194 194 fg = self.colormap[style.fg]
195 195 dc.SetTextForeground(fg)
196 196 (w, h) = dc.GetTextExtent(part)
197 197 dc.DrawText(part, x, y)
198 198 x += w
199 199 elif align == 0: # center alignment
200 200 (width, height) = dc.GetTextExtent(str(text))
201 201 x = rect[0]+0.5*(rect[2]-width)
202 202 y = rect[1]+0.5*(rect[3]-height)
203 203 for (style, part) in text:
204 204 if isSelected:
205 205 fg = grid.GetSelectionForeground()
206 206 else:
207 207 fg = self.colormap[style.fg]
208 208 dc.SetTextForeground(fg)
209 209 (w, h) = dc.GetTextExtent(part)
210 210 dc.DrawText(part, x, y)
211 211 x += w
212 212 else: # right alignment
213 213 (width, height) = dc.GetTextExtent(str(text))
214 214 x = rect[0]+rect[2]-1
215 215 y = rect[1]+0.5*(rect[3]-height)
216 216 for (style, part) in reversed(text):
217 217 (w, h) = dc.GetTextExtent(part)
218 218 x -= w
219 219 if isSelected:
220 220 fg = grid.GetSelectionForeground()
221 221 else:
222 222 fg = self.colormap[style.fg]
223 223 dc.SetTextForeground(fg)
224 224 dc.DrawText(part, x, y)
225 225 dc.DestroyClippingRegion()
226 226
227 227 def Clone(self):
228 228 return IGridRenderer(self.table)
229 229
230 230
231 231 class IGridTable(wx.grid.PyGridTableBase):
232 232 # The data table for the ``IGridGrid``. Some dirty tricks were used here:
233 233 # ``GetValue()`` does not get any values (or at least it does not return
234 234 # anything, accessing the values is done by the renderer)
235 235 # but rather tries to fetch the objects which were requested into the table.
236 236 # General behaviour is: Fetch the first X objects. If the user scrolls down
237 237 # to the last object another bunch of X objects is fetched (if possible)
238 238 def __init__(self, input, fontsize, *attrs):
239 239 wx.grid.PyGridTableBase.__init__(self)
240 240 self.input = input
241 241 self.iterator = ipipe.xiter(input)
242 242 self.items = []
243 243 self.attrs = [ipipe.upgradexattr(attr) for attr in attrs]
244 244 self._displayattrs = self.attrs[:]
245 245 self._displayattrset = set(self.attrs)
246 246 self.fontsize = fontsize
247 247 self._fetch(1)
248 248 self.timer = wx.Timer()
249 249 self.timer.Bind(wx.EVT_TIMER, self.refresh_content)
250 250
251 251 def GetAttr(self, *args):
252 252 attr = wx.grid.GridCellAttr()
253 253 attr.SetFont(wx.Font(self.fontsize, wx.TELETYPE, wx.NORMAL, wx.NORMAL))
254 254 return attr
255 255
256 256 def GetNumberRows(self):
257 257 return len(self.items)
258 258
259 259 def GetNumberCols(self):
260 260 return len(self._displayattrs)
261 261
262 262 def GetColLabelValue(self, col):
263 263 if col < len(self._displayattrs):
264 264 return self._displayattrs[col].name()
265 265 else:
266 266 return ""
267 267
268 268 def GetRowLabelValue(self, row):
269 269 return str(row)
270 270
271 271 def IsEmptyCell(self, row, col):
272 272 return False
273 273
274 274 def _append(self, item):
275 275 self.items.append(item)
276 276 # Nothing to do if the set of attributes has been fixed by the user
277 277 if not self.attrs:
278 278 for attr in ipipe.xattrs(item):
279 279 attr = ipipe.upgradexattr(attr)
280 280 if attr not in self._displayattrset:
281 281 self._displayattrs.append(attr)
282 282 self._displayattrset.add(attr)
283 283
284 284 def _fetch(self, count):
285 285 # Try to fill ``self.items`` with at least ``count`` objects.
286 286 have = len(self.items)
287 287 while self.iterator is not None and have < count:
288 288 try:
289 289 item = self.iterator.next()
290 290 except StopIteration:
291 291 self.iterator = None
292 292 break
293 293 except (KeyboardInterrupt, SystemExit):
294 294 raise
295 295 except Exception, exc:
296 296 have += 1
297 297 self._append(exc)
298 298 self.iterator = None
299 299 break
300 300 else:
301 301 have += 1
302 302 self._append(item)
303 303
304 304 def GetValue(self, row, col):
305 305 # some kind of dummy-function: does not return anything but "";
306 306 # (The value isn't use anyway)
307 307 # its main task is to trigger the fetch of new objects
308 308 sizing_needed = False
309 309 had_cols = len(self._displayattrs)
310 310 had_rows = len(self.items)
311 311 if row == had_rows - 1 and self.iterator is not None:
312 312 self._fetch(row + 20)
313 313 sizing_needed = True
314 314 have_rows = len(self.items)
315 315 have_cols = len(self._displayattrs)
316 316 if have_rows > had_rows:
317 317 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, have_rows - had_rows)
318 318 self.GetView().ProcessTableMessage(msg)
319 319 sizing_needed = True
320 320 if row >= have_rows:
321 321 return ""
322 322 if have_cols != had_cols:
323 323 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED, have_cols - had_cols)
324 324 self.GetView().ProcessTableMessage(msg)
325 325 sizing_needed = True
326 326 if sizing_needed:
327 327 self.GetView().AutoSizeColumns(False)
328 328 return ""
329 329
330 330 def SetValue(self, row, col, value):
331 331 pass
332 332
333 333 def refresh_content(self, event):
334 334 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, 0, self.GetNumberRows())
335 335 self.GetView().ProcessTableMessage(msg)
336 336 self.iterator = ipipe.xiter(self.input)
337 337 self.items = []
338 338 self.attrs = [] # _append will calculate new displayattrs
339 339 self._fetch(1) # fetch one...
340 340 if self.items:
341 341 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, 1)
342 342 self.GetView().ProcessTableMessage(msg)
343 343 self.GetValue(0, 0) # and trigger "fetch next 20"
344 344 item = self.items[0]
345 345 self.GetView().AutoSizeColumns(False)
346 346 panel = self.GetView().GetParent()
347 347 nb = panel.GetParent()
348 348 current = nb.GetSelection()
349 349 if nb.GetPage(current) == panel:
350 350 self.GetView().set_footer(item)
351 351
352 352 class IGridGrid(wx.grid.Grid):
353 353 # The actual grid
354 354 # all methods for selecting/sorting/picking/... data are implemented here
355 355 def __init__(self, panel, input, *attrs):
356 356 wx.grid.Grid.__init__(self, panel)
357 357 fontsize = 9
358 358 self.input = input
359 359 self.table = IGridTable(self.input, fontsize, *attrs)
360 360 self.SetTable(self.table, True)
361 361 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
362 362 self.SetDefaultRenderer(IGridRenderer(self.table))
363 363 self.EnableEditing(False)
364 364 self.Bind(wx.EVT_KEY_DOWN, self.key_pressed)
365 365 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.cell_doubleclicked)
366 366 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.cell_leftclicked)
367 367 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_DCLICK, self.label_doubleclicked)
368 368 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_label_leftclick)
369 369 self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self._on_selected_range)
370 370 self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_selected_cell)
371 371 self.current_selection = set()
372 372 self.maxchars = 200
373 373
374 374 def on_label_leftclick(self, event):
375 375 event.Skip()
376 376
377 377 def error_output(self, text):
378 378 wx.Bell()
379 379 frame = self.GetParent().GetParent().GetParent()
380 380 frame.SetStatusText(str(text))
381 381
382 382 def _on_selected_range(self, event):
383 383 # Internal update to the selection tracking lists
384 384 if event.Selecting():
385 385 # adding to the list...
386 386 self.current_selection.update(xrange(event.GetTopRow(), event.GetBottomRow()+1))
387 387 else:
388 388 # removal from list
389 389 for index in xrange(event.GetTopRow(), event.GetBottomRow()+1):
390 390 self.current_selection.discard(index)
391 391 event.Skip()
392 392
393 393 def _on_selected_cell(self, event):
394 394 # Internal update to the selection tracking list
395 395 self.current_selection = set([event.GetRow()])
396 396 event.Skip()
397 397
398 398 def sort(self, key, reverse=False):
399 399 """
400 400 Sort the current list of items using the key function ``key``. If
401 401 ``reverse`` is true the sort order is reversed.
402 402 """
403 403 row = self.GetGridCursorRow()
404 404 col = self.GetGridCursorCol()
405 405 curitem = self.table.items[row] # Remember where the cursor is now
406 406 # Sort items
407 407 def realkey(item):
408 408 try:
409 409 return key(item)
410 410 except (KeyboardInterrupt, SystemExit):
411 411 raise
412 412 except Exception:
413 413 return None
414 414 try:
415 415 self.table.items = ipipe.deque(sorted(self.table.items, key=realkey, reverse=reverse))
416 416 except TypeError, exc:
417 417 self.error_output("Exception encountered: %s" % exc)
418 418 return
419 419 # Find out where the object under the cursor went
420 420 for (i, item) in enumerate(self.table.items):
421 421 if item is curitem:
422 422 self.SetGridCursor(i,col)
423 423 self.MakeCellVisible(i,col)
424 424 self.Refresh()
425 425
426 426 def sortattrasc(self):
427 427 """
428 428 Sort in ascending order; sorting criteria is the current attribute
429 429 """
430 430 col = self.GetGridCursorCol()
431 431 attr = self.table._displayattrs[col]
432 432 frame = self.GetParent().GetParent().GetParent()
433 433 if attr is ipipe.noitem:
434 434 self.error_output("no column under cursor")
435 435 return
436 436 frame.SetStatusText("sort by %s (ascending)" % attr.name())
437 437 def key(item):
438 438 try:
439 439 return attr.value(item)
440 440 except (KeyboardInterrupt, SystemExit):
441 441 raise
442 442 except Exception:
443 443 return None
444 444 self.sort(key)
445 445
446 446 def sortattrdesc(self):
447 447 """
448 448 Sort in descending order; sorting criteria is the current attribute
449 449 """
450 450 col = self.GetGridCursorCol()
451 451 attr = self.table._displayattrs[col]
452 452 frame = self.GetParent().GetParent().GetParent()
453 453 if attr is ipipe.noitem:
454 454 self.error_output("no column under cursor")
455 455 return
456 456 frame.SetStatusText("sort by %s (descending)" % attr.name())
457 457 def key(item):
458 458 try:
459 459 return attr.value(item)
460 460 except (KeyboardInterrupt, SystemExit):
461 461 raise
462 462 except Exception:
463 463 return None
464 464 self.sort(key, reverse=True)
465 465
466 466 def label_doubleclicked(self, event):
467 467 row = event.GetRow()
468 468 col = event.GetCol()
469 469 if col == -1:
470 470 self.enter(row)
471 471
472 472 def _getvalue(self, row, col):
473 473 """
474 474 Gets the text which is displayed at ``(row, col)``
475 475 """
476 476 try:
477 477 value = self.table._displayattrs[col].value(self.table.items[row])
478 478 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
479 479 except IndexError:
480 480 raise IndexError
481 481 except Exception, exc:
482 482 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
483 483 return text
484 484
485 485 def searchexpression(self, searchexp, startrow=None, search_forward=True ):
486 486 """
487 487 Find by expression
488 488 """
489 489 frame = self.GetParent().GetParent().GetParent()
490 490 if searchexp:
491 491 if search_forward:
492 492 if not startrow:
493 493 row = self.GetGridCursorRow()+1
494 494 else:
495 495 row = startrow + 1
496 496 while True:
497 497 try:
498 498 foo = self.table.GetValue(row, 0)
499 499 item = self.table.items[row]
500 500 try:
501 501 globals = ipipe.getglobals(None)
502 502 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
503 503 self.SetGridCursor(row, 0) # found something
504 504 self.MakeCellVisible(row, 0)
505 505 break
506 506 except (KeyboardInterrupt, SystemExit):
507 507 raise
508 508 except Exception, exc:
509 509 frame.SetStatusText(str(exc))
510 510 wx.Bell()
511 511 break # break on error
512 512 except IndexError:
513 513 return
514 514 row += 1
515 515 else:
516 516 if not startrow:
517 517 row = self.GetGridCursorRow() - 1
518 518 else:
519 519 row = startrow - 1
520 520 while True:
521 521 try:
522 522 foo = self.table.GetValue(row, 0)
523 523 item = self.table.items[row]
524 524 try:
525 525 globals = ipipe.getglobals(None)
526 526 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
527 527 self.SetGridCursor(row, 0) # found something
528 528 self.MakeCellVisible(row, 0)
529 529 break
530 530 except (KeyboardInterrupt, SystemExit):
531 531 raise
532 532 except Exception, exc:
533 533 frame.SetStatusText(str(exc))
534 534 wx.Bell()
535 535 break # break on error
536 536 except IndexError:
537 537 return
538 538 row -= 1
539 539
540 540
541 541 def search(self, searchtext, startrow=None, startcol=None, search_forward=True):
542 542 """
543 543 search for ``searchtext``, starting in ``(startrow, startcol)``;
544 544 if ``search_forward`` is true the direction is "forward"
545 545 """
546 546 searchtext = searchtext.lower()
547 547 if search_forward:
548 548 if startrow is not None and startcol is not None:
549 549 row = startrow
550 550 else:
551 551 startcol = self.GetGridCursorCol() + 1
552 552 row = self.GetGridCursorRow()
553 553 if startcol >= self.GetNumberCols():
554 554 startcol = 0
555 555 row += 1
556 556 while True:
557 557 for col in xrange(startcol, self.table.GetNumberCols()):
558 558 try:
559 559 foo = self.table.GetValue(row, col)
560 560 text = self._getvalue(row, col)
561 561 if searchtext in text.string().lower():
562 562 self.SetGridCursor(row, col)
563 563 self.MakeCellVisible(row, col)
564 564 return
565 565 except IndexError:
566 566 return
567 567 startcol = 0
568 568 row += 1
569 569 else:
570 570 if startrow is not None and startcol is not None:
571 571 row = startrow
572 572 else:
573 573 startcol = self.GetGridCursorCol() - 1
574 574 row = self.GetGridCursorRow()
575 575 if startcol < 0:
576 576 startcol = self.GetNumberCols() - 1
577 577 row -= 1
578 578 while True:
579 579 for col in xrange(startcol, -1, -1):
580 580 try:
581 581 foo = self.table.GetValue(row, col)
582 582 text = self._getvalue(row, col)
583 583 if searchtext in text.string().lower():
584 584 self.SetGridCursor(row, col)
585 585 self.MakeCellVisible(row, col)
586 586 return
587 587 except IndexError:
588 588 return
589 589 startcol = self.table.GetNumberCols()-1
590 590 row -= 1
591 591
592 592 def key_pressed(self, event):
593 593 """
594 594 Maps pressed keys to functions
595 595 """
596 596 frame = self.GetParent().GetParent().GetParent()
597 597 frame.SetStatusText("")
598 598 sh = event.ShiftDown()
599 599 ctrl = event.ControlDown()
600 600
601 601 keycode = event.GetKeyCode()
602 602 if keycode == ord("P"):
603 603 row = self.GetGridCursorRow()
604 604 if sh:
605 605 col = self.GetGridCursorCol()
606 606 self.pickattr(row, col)
607 607 else:
608 608 self.pick(row)
609 609 elif keycode == ord("M"):
610 610 if ctrl:
611 611 col = self.GetGridCursorCol()
612 612 self.pickrowsattr(sorted(self.current_selection), col)
613 613 else:
614 614 self.pickrows(sorted(self.current_selection))
615 615 elif keycode in (wx.WXK_BACK, wx.WXK_DELETE, ord("X")) and not (ctrl or sh):
616 616 self.delete_current_notebook()
617 617 elif keycode in (ord("E"), ord("\r")):
618 618 row = self.GetGridCursorRow()
619 619 if sh:
620 620 col = self.GetGridCursorCol()
621 621 self.enterattr(row, col)
622 622 else:
623 623 self.enter(row)
624 624 elif keycode == ord("E") and ctrl:
625 625 row = self.GetGridCursorRow()
626 626 self.SetGridCursor(row, self.GetNumberCols()-1)
627 627 elif keycode == wx.WXK_HOME or (keycode == ord("A") and ctrl):
628 628 row = self.GetGridCursorRow()
629 629 self.SetGridCursor(row, 0)
630 630 elif keycode == ord("C") and sh:
631 631 col = self.GetGridCursorCol()
632 632 attr = self.table._displayattrs[col]
633 633 result = []
634 634 for i in xrange(self.GetNumberRows()):
635 635 result.append(self.table._displayattrs[col].value(self.table.items[i]))
636 636 self.quit(result)
637 637 elif keycode in (wx.WXK_ESCAPE, ord("Q")) and not (ctrl or sh):
638 638 self.quit()
639 639 elif keycode == ord("<"):
640 640 row = self.GetGridCursorRow()
641 641 col = self.GetGridCursorCol()
642 642 if not event.ShiftDown():
643 643 newcol = col - 1
644 644 if newcol >= 0:
645 645 self.SetGridCursor(row, col - 1)
646 646 else:
647 647 newcol = col + 1
648 648 if newcol < self.GetNumberCols():
649 649 self.SetGridCursor(row, col + 1)
650 650 elif keycode == ord("D"):
651 651 col = self.GetGridCursorCol()
652 652 row = self.GetGridCursorRow()
653 653 if not sh:
654 654 self.detail(row, col)
655 655 else:
656 656 self.detail_attr(row, col)
657 657 elif keycode == ord("F") and ctrl:
658 658 if sh:
659 659 frame.enter_searchexpression(event)
660 660 else:
661 661 frame.enter_searchtext(event)
662 662 elif keycode == wx.WXK_F3:
663 663 if sh:
664 664 frame.find_previous(event)
665 665 else:
666 666 frame.find_next(event)
667 667 elif keycode == ord("V"):
668 668 if sh:
669 669 self.sortattrdesc()
670 670 else:
671 671 self.sortattrasc()
672 672 elif keycode == wx.WXK_DOWN:
673 673 row = self.GetGridCursorRow()
674 674 try:
675 675 item = self.table.items[row+1]
676 676 except IndexError:
677 677 item = self.table.items[row]
678 678 self.set_footer(item)
679 679 event.Skip()
680 680 elif keycode == wx.WXK_UP:
681 681 row = self.GetGridCursorRow()
682 682 if row >= 1:
683 683 item = self.table.items[row-1]
684 684 else:
685 685 item = self.table.items[row]
686 686 self.set_footer(item)
687 687 event.Skip()
688 688 elif keycode == wx.WXK_RIGHT:
689 689 row = self.GetGridCursorRow()
690 690 item = self.table.items[row]
691 691 self.set_footer(item)
692 692 event.Skip()
693 693 elif keycode == wx.WXK_LEFT:
694 694 row = self.GetGridCursorRow()
695 695 item = self.table.items[row]
696 696 self.set_footer(item)
697 697 event.Skip()
698 698 elif keycode == ord("R") or keycode == wx.WXK_F5:
699 699 self.table.refresh_content(event)
700 700 elif keycode == ord("I"):
701 701 row = self.GetGridCursorRow()
702 702 if not sh:
703 703 self.pickinput(row)
704 704 else:
705 705 col = self.GetGridCursorCol()
706 706 self.pickinputattr(row, col)
707 707 else:
708 708 event.Skip()
709 709
710 710 def delete_current_notebook(self):
711 711 """
712 712 deletes the current notebook tab
713 713 """
714 714 panel = self.GetParent()
715 715 nb = panel.GetParent()
716 716 current = nb.GetSelection()
717 717 count = nb.GetPageCount()
718 718 if count > 1:
719 719 for i in xrange(count-1, current-1, -1):
720 720 nb.DeletePage(i)
721 721 nb.GetCurrentPage().grid.SetFocus()
722 722 else:
723 723 frame = nb.GetParent()
724 724 frame.SetStatusText("This is the last level!")
725 725
726 726 def _doenter(self, value, *attrs):
727 727 """
728 728 "enter" a special item resulting in a new notebook tab
729 729 """
730 730 panel = self.GetParent()
731 731 nb = panel.GetParent()
732 732 frame = nb.GetParent()
733 733 current = nb.GetSelection()
734 734 count = nb.GetPageCount()
735 735 try: # if we want to enter something non-iterable, e.g. a function
736 736 if current + 1 == count and value is not self.input: # we have an event in the last tab
737 737 frame._add_notebook(value, *attrs)
738 738 elif value != self.input: # we have to delete all tabs newer than [panel] first
739 739 for i in xrange(count-1, current, -1): # some tabs don't close if we don't close in *reverse* order
740 740 nb.DeletePage(i)
741 741 frame._add_notebook(value)
742 742 except TypeError, exc:
743 743 if exc.__class__.__module__ == "exceptions":
744 744 msg = "%s: %s" % (exc.__class__.__name__, exc)
745 745 else:
746 746 msg = "%s.%s: %s" % (exc.__class__.__module__, exc.__class__.__name__, exc)
747 747 frame.SetStatusText(msg)
748 748
749 749 def enterattr(self, row, col):
750 750 try:
751 751 attr = self.table._displayattrs[col]
752 752 value = attr.value(self.table.items[row])
753 753 except Exception, exc:
754 754 self.error_output(str(exc))
755 755 else:
756 756 self._doenter(value)
757 757
758 758 def set_footer(self, item):
759 759 frame = self.GetParent().GetParent().GetParent()
760 760 frame.SetStatusText(" ".join([str(text) for (style, text) in ipipe.xformat(item, "footer", 20)[2]]), 0)
761 761
762 762 def enter(self, row):
763 763 try:
764 764 value = self.table.items[row]
765 765 except Exception, exc:
766 766 self.error_output(str(exc))
767 767 else:
768 768 self._doenter(value)
769 769
770 770 def detail(self, row, col):
771 771 """
772 772 shows a detail-view of the current cell
773 773 """
774 774 try:
775 775 attr = self.table._displayattrs[col]
776 776 item = self.table.items[row]
777 777 except Exception, exc:
778 778 self.error_output(str(exc))
779 779 else:
780 780 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
781 781 self._doenter(attrs)
782 782
783 783 def detail_attr(self, row, col):
784 784 try:
785 785 attr = self.table._displayattrs[col]
786 786 item = attr.value(self.table.items[row])
787 787 except Exception, exc:
788 788 self.error_output(str(exc))
789 789 else:
790 790 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
791 791 self._doenter(attrs)
792 792
793 793 def quit(self, result=None):
794 794 """
795 795 quit
796 796 """
797 797 frame = self.GetParent().GetParent().GetParent()
798 798 if frame.helpdialog:
799 799 frame.helpdialog.Destroy()
800 800 app = frame.parent
801 801 if app is not None:
802 802 app.result = result
803 803 frame.Close()
804 804 frame.Destroy()
805 805
806 806 def cell_doubleclicked(self, event):
807 807 self.enterattr(event.GetRow(), event.GetCol())
808 808 event.Skip()
809 809
810 810 def cell_leftclicked(self, event):
811 811 row = event.GetRow()
812 812 item = self.table.items[row]
813 813 self.set_footer(item)
814 814 event.Skip()
815 815
816 816 def pick(self, row):
817 817 """
818 818 pick a single row and return to the IPython prompt
819 819 """
820 820 try:
821 821 value = self.table.items[row]
822 822 except Exception, exc:
823 823 self.error_output(str(exc))
824 824 else:
825 825 self.quit(value)
826 826
827 827 def pickinput(self, row):
828 828 try:
829 829 value = self.table.items[row]
830 830 except Exception, exc:
831 831 self.error_output(str(exc))
832 832 else:
833 833 api = ipapi.get()
834 834 api.set_next_input(str(value))
835 835 self.quit(value)
836 836
837 837 def pickinputattr(self, row, col):
838 838 try:
839 839 attr = self.table._displayattrs[col]
840 840 value = attr.value(self.table.items[row])
841 841 except Exception, exc:
842 842 self.error_output(str(exc))
843 843 else:
844 844 api = ipapi.get()
845 845 api.set_next_input(str(value))
846 846 self.quit(value)
847 847
848 848 def pickrows(self, rows):
849 849 """
850 850 pick multiple rows and return to the IPython prompt
851 851 """
852 852 try:
853 853 value = [self.table.items[row] for row in rows]
854 854 except Exception, exc:
855 855 self.error_output(str(exc))
856 856 else:
857 857 self.quit(value)
858 858
859 859 def pickrowsattr(self, rows, col):
860 860 """"
861 861 pick one column from multiple rows
862 862 """
863 863 values = []
864 864 try:
865 865 attr = self.table._displayattrs[col]
866 866 for row in rows:
867 867 try:
868 868 values.append(attr.value(self.table.items[row]))
869 869 except (SystemExit, KeyboardInterrupt):
870 870 raise
871 871 except Exception:
872 872 raise #pass
873 873 except Exception, exc:
874 874 self.error_output(str(exc))
875 875 else:
876 876 self.quit(values)
877 877
878 878 def pickattr(self, row, col):
879 879 try:
880 880 attr = self.table._displayattrs[col]
881 881 value = attr.value(self.table.items[row])
882 882 except Exception, exc:
883 883 self.error_output(str(exc))
884 884 else:
885 885 self.quit(value)
886 886
887 887
888 888 class IGridPanel(wx.Panel):
889 889 # Each IGridPanel contains an IGridGrid
890 890 def __init__(self, parent, input, *attrs):
891 891 wx.Panel.__init__(self, parent, -1)
892 892 self.grid = IGridGrid(self, input, *attrs)
893 893 self.grid.FitInside()
894 894 sizer = wx.BoxSizer(wx.VERTICAL)
895 895 sizer.Add(self.grid, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
896 896 self.SetSizer(sizer)
897 897 sizer.Fit(self)
898 898 sizer.SetSizeHints(self)
899 899
900 900
901 901 class IGridHTMLHelp(wx.Frame):
902 902 def __init__(self, parent, title, size):
903 903 wx.Frame.__init__(self, parent, -1, title, size=size)
904 904 html = wx.html.HtmlWindow(self)
905 905 if "gtk2" in wx.PlatformInfo:
906 906 html.SetStandardFonts()
907 907 html.SetPage(help)
908 908
909 909
910 910 class IGridFrame(wx.Frame):
911 911 maxtitlelen = 30
912 912
913 913 def __init__(self, parent, input):
914 914 title = " ".join([str(text) for (style, text) in ipipe.xformat(input, "header", 20)[2]])
915 915 wx.Frame.__init__(self, None, title=title, size=(640, 480))
916 916 self.menubar = wx.MenuBar()
917 917 self.menucounter = 100
918 918 self.m_help = wx.Menu()
919 919 self.m_search = wx.Menu()
920 920 self.m_sort = wx.Menu()
921 921 self.m_refresh = wx.Menu()
922 922 self.notebook = wx.Notebook(self, -1, style=0)
923 923 self.statusbar = self.CreateStatusBar(1, wx.ST_SIZEGRIP)
924 924 self.statusbar.SetFieldsCount(2)
925 925 self.SetStatusWidths([-1, 200])
926 926 self.parent = parent
927 927 self._add_notebook(input)
928 928 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
929 929 self.makemenu(self.m_sort, "&Sort (asc)\tV", "Sort ascending", self.sortasc)
930 930 self.makemenu(self.m_sort, "Sort (&desc)\tShift-V", "Sort descending", self.sortdesc)
931 931 self.makemenu(self.m_help, "&Help\tF1", "Help", self.display_help)
932 932 # self.makemenu(self.m_help, "&Show help in browser", "Show help in browser", self.display_help_in_browser)
933 933 self.makemenu(self.m_search, "&Find text\tCTRL-F", "Find text", self.enter_searchtext)
934 934 self.makemenu(self.m_search, "Find by &expression\tCTRL-Shift-F", "Find by expression", self.enter_searchexpression)
935 935 self.makemenu(self.m_search, "Find &next\tF3", "Find next", self.find_next)
936 936 self.makemenu(self.m_search, "Find &previous\tShift-F3", "Find previous", self.find_previous)
937 937 self.makemenu(self.m_refresh, "&Refresh once \tF5", "Refresh once", self.refresh_once)
938 938 self.makemenu(self.m_refresh, "Refresh every &1s", "Refresh every second", self.refresh_every_second)
939 939 self.makemenu(self.m_refresh, "Refresh every &X seconds", "Refresh every X seconds", self.refresh_interval)
940 940 self.makemenu(self.m_refresh, "&Stop all refresh timers", "Stop refresh timers", self.stop_refresh)
941 941 self.menubar.Append(self.m_search, "&Find")
942 942 self.menubar.Append(self.m_sort, "&Sort")
943 943 self.menubar.Append(self.m_refresh, "&Refresh")
944 944 self.menubar.Append(self.m_help, "&Help")
945 945 self.SetMenuBar(self.menubar)
946 946 self.searchtext = ""
947 947 self.searchexpression = ""
948 948 self.helpdialog = None
949 949 self.refresh_interval = 1000
950 950 self.SetStatusText("Refreshing inactive", 1)
951 951
952 952 def refresh_once(self, event):
953 953 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
954 954 table.refresh_content(event)
955 955
956 956 def refresh_interval(self, event):
957 957 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
958 958 dlg = wx.TextEntryDialog(self, "Enter refresh interval (milliseconds):", "Refresh timer:", defaultValue=str(self.refresh_interval))
959 959 if dlg.ShowModal() == wx.ID_OK:
960 960 try:
961 961 milliseconds = int(dlg.GetValue())
962 962 except ValueError, exc:
963 963 self.SetStatusText(str(exc))
964 964 else:
965 965 table.timer.Start(milliseconds=milliseconds, oneShot=False)
966 966 self.SetStatusText("Refresh timer set to %s ms" % milliseconds)
967 967 self.SetStatusText("Refresh interval: %s ms" % milliseconds, 1)
968 968 self.refresh_interval = milliseconds
969 969 dlg.Destroy()
970 970
971 971 def stop_refresh(self, event):
972 972 for i in xrange(self.notebook.GetPageCount()):
973 973 nb = self.notebook.GetPage(i)
974 974 nb.grid.table.timer.Stop()
975 975 self.SetStatusText("Refreshing inactive", 1)
976 976
977 977 def refresh_every_second(self, event):
978 978 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
979 979 table.timer.Start(milliseconds=1000, oneShot=False)
980 980 self.SetStatusText("Refresh interval: 1000 ms", 1)
981 981
982 982 def sortasc(self, event):
983 983 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
984 984 grid.sortattrasc()
985 985
986 986 def sortdesc(self, event):
987 987 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
988 988 grid.sortattrdesc()
989 989
990 990 def find_previous(self, event):
991 991 """
992 992 find previous occurrences
993 993 """
994 994 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
995 995 if self.searchtext:
996 996 row = grid.GetGridCursorRow()
997 997 col = grid.GetGridCursorCol()
998 998 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
999 999 if col-1 >= 0:
1000 1000 grid.search(self.searchtext, row, col-1, False)
1001 1001 else:
1002 1002 grid.search(self.searchtext, row-1, grid.table.GetNumberCols()-1, False)
1003 1003 elif self.searchexpression:
1004 1004 self.SetStatusText("Search mode: expression; looking for %s" % repr(self.searchexpression)[2:-1])
1005 1005 grid.searchexpression(searchexp=self.searchexpression, search_forward=False)
1006 1006 else:
1007 1007 self.SetStatusText("No search yet: please enter search-text or -expression")
1008 1008
1009 1009 def find_next(self, event):
1010 1010 """
1011 1011 find the next occurrence
1012 1012 """
1013 1013 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
1014 1014 if self.searchtext != "":
1015 1015 row = grid.GetGridCursorRow()
1016 1016 col = grid.GetGridCursorCol()
1017 1017 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
1018 1018 if col+1 < grid.table.GetNumberCols():
1019 1019 grid.search(self.searchtext, row, col+1)
1020 1020 else:
1021 1021 grid.search(self.searchtext, row+1, 0)
1022 1022 elif self.searchexpression != "":
1023 1023 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1024 1024 grid.searchexpression(searchexp=self.searchexpression)
1025 1025 else:
1026 1026 self.SetStatusText("No search yet: please enter search-text or -expression")
1027 1027
1028 1028 def display_help(self, event):
1029 1029 """
1030 1030 Display a help dialog
1031 1031 """
1032 1032 if self.helpdialog:
1033 1033 self.helpdialog.Destroy()
1034 1034 self.helpdialog = IGridHTMLHelp(None, title="Help", size=wx.Size(600,400))
1035 1035 self.helpdialog.Show()
1036 1036
1037 1037 def display_help_in_browser(self, event):
1038 1038 """
1039 1039 Show the help-HTML in a browser (as a ``HtmlWindow`` does not understand
1040 1040 CSS this looks better)
1041 1041 """
1042 1042 filename = urllib.pathname2url(os.path.abspath(os.path.join(os.path.dirname(__file__), "igrid_help.html")))
1043 1043 if not filename.startswith("file"):
1044 1044 filename = "file:" + filename
1045 1045 webbrowser.open(filename, new=1, autoraise=True)
1046 1046
1047 1047 def enter_searchexpression(self, event):
1048 1048 dlg = wx.TextEntryDialog(self, "Find:", "Find matching expression:", defaultValue=self.searchexpression)
1049 1049 if dlg.ShowModal() == wx.ID_OK:
1050 1050 self.searchexpression = dlg.GetValue()
1051 1051 self.searchtext = ""
1052 1052 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1053 1053 self.notebook.GetPage(self.notebook.GetSelection()).grid.searchexpression(self.searchexpression)
1054 1054 dlg.Destroy()
1055 1055
1056 1056 def makemenu(self, menu, label, help, cmd):
1057 1057 menu.Append(self.menucounter, label, help)
1058 1058 self.Bind(wx.EVT_MENU, cmd, id=self.menucounter)
1059 1059 self.menucounter += 1
1060 1060
1061 1061 def _add_notebook(self, input, *attrs):
1062 1062 # Adds another notebook which has the starting object ``input``
1063 1063 panel = IGridPanel(self.notebook, input, *attrs)
1064 1064 text = str(ipipe.xformat(input, "header", self.maxtitlelen)[2])
1065 1065 if len(text) >= self.maxtitlelen:
1066 1066 text = text[:self.maxtitlelen].rstrip(".") + "..."
1067 1067 self.notebook.AddPage(panel, text, True)
1068 1068 panel.grid.SetFocus()
1069 1069 self.Layout()
1070 1070
1071 1071 def OnCloseWindow(self, event):
1072 1072 self.Destroy()
1073 1073
1074 1074 def enter_searchtext(self, event):
1075 1075 # Displays a dialog asking for the searchtext
1076 1076 dlg = wx.TextEntryDialog(self, "Find:", "Find in list", defaultValue=self.searchtext)
1077 1077 if dlg.ShowModal() == wx.ID_OK:
1078 1078 self.searchtext = dlg.GetValue()
1079 1079 self.searchexpression = ""
1080 1080 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
1081 1081 self.notebook.GetPage(self.notebook.GetSelection()).grid.search(self.searchtext)
1082 1082 dlg.Destroy()
1083 1083
1084 1084
1085 1085 class App(wx.App):
1086 1086 def __init__(self, input):
1087 1087 self.input = input
1088 1088 self.result = None # Result to be returned to IPython. Set by quit().
1089 1089 wx.App.__init__(self)
1090 1090
1091 1091 def OnInit(self):
1092 1092 frame = IGridFrame(self, self.input)
1093 1093 frame.Show()
1094 1094 self.SetTopWindow(frame)
1095 1095 frame.Raise()
1096 1096 return True
1097 1097
1098 1098
1099 1099 class igrid(ipipe.Display):
1100 1100 """
1101 1101 This is a wx-based display object that can be used instead of ``ibrowse``
1102 1102 (which is curses-based) or ``idump`` (which simply does a print).
1103 1103 """
1104 1104
1105 1105 if wx.VERSION < (2, 7):
1106 1106 def display(self):
1107 1107 try:
1108 1108 # Try to create a "standalone" frame. If this works we're probably
1109 1109 # running with -wthread.
1110 1110 # Note that this sets the parent of the frame to None, but we can't
1111 1111 # pass a result object back to the shell anyway.
1112 1112 frame = IGridFrame(None, self.input)
1113 1113 frame.Show()
1114 1114 frame.Raise()
1115 1115 except wx.PyNoAppError:
1116 1116 # There's no wx application yet => create one.
1117 1117 app = App(self.input)
1118 1118 app.MainLoop()
1119 1119 return app.result
1120 1120 else:
1121 1121 # With wx 2.7 it gets simpler.
1122 1122 def display(self):
1123 1123 app = App(self.input)
1124 1124 app.MainLoop()
1125 1125 return app.result
1126 1126
@@ -1,2328 +1,2328 b''
1 1 # -*- coding: iso-8859-1 -*-
2 2
3 3 """
4 4 ``ipipe`` provides classes to be used in an interactive Python session. Doing a
5 5 ``from ipipe import *`` is the preferred way to do this. The name of all
6 6 objects imported this way starts with ``i`` to minimize collisions.
7 7
8 8 ``ipipe`` supports "pipeline expressions", which is something resembling Unix
9 9 pipes. An example is::
10 10
11 11 >>> ienv | isort("key.lower()")
12 12
13 13 This gives a listing of all environment variables sorted by name.
14 14
15 15
16 16 There are three types of objects in a pipeline expression:
17 17
18 18 * ``Table``s: These objects produce items. Examples are ``ils`` (listing the
19 19 current directory, ``ienv`` (listing environment variables), ``ipwd`` (listing
20 20 user accounts) and ``igrp`` (listing user groups). A ``Table`` must be the
21 21 first object in a pipe expression.
22 22
23 23 * ``Pipe``s: These objects sit in the middle of a pipe expression. They
24 24 transform the input in some way (e.g. filtering or sorting it). Examples are:
25 25 ``ifilter`` (which filters the input pipe), ``isort`` (which sorts the input
26 26 pipe) and ``ieval`` (which evaluates a function or expression for each object
27 27 in the input pipe).
28 28
29 29 * ``Display``s: These objects can be put as the last object in a pipeline
30 30 expression. There are responsible for displaying the result of the pipeline
31 31 expression. If a pipeline expression doesn't end in a display object a default
32 32 display objects will be used. One example is ``ibrowse`` which is a ``curses``
33 33 based browser.
34 34
35 35
36 36 Adding support for pipeline expressions to your own objects can be done through
37 37 three extensions points (all of them optional):
38 38
39 39 * An object that will be displayed as a row by a ``Display`` object should
40 40 implement the method ``__xattrs__(self, mode)`` method or register an
41 41 implementation of the generic function ``xattrs``. For more info see ``xattrs``.
42 42
43 43 * When an object ``foo`` is displayed by a ``Display`` object, the generic
44 44 function ``xrepr`` is used.
45 45
46 46 * Objects that can be iterated by ``Pipe``s must iterable. For special cases,
47 47 where iteration for display is different than the normal iteration a special
48 48 implementation can be registered with the generic function ``xiter``. This
49 49 makes it possible to use dictionaries and modules in pipeline expressions,
50 50 for example::
51 51
52 52 >>> import sys
53 53 >>> sys | ifilter("isinstance(value, int)") | idump
54 54 key |value
55 55 api_version| 1012
56 56 dllhandle | 503316480
57 57 hexversion | 33817328
58 58 maxint |2147483647
59 59 maxunicode | 65535
60 60 >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()")
61 61 ...
62 62
63 63 Note: The expression strings passed to ``ifilter()`` and ``isort()`` can
64 64 refer to the object to be filtered or sorted via the variable ``_`` and to any
65 65 of the attributes of the object, i.e.::
66 66
67 67 >>> sys.modules | ifilter("_.value is not None") | isort("_.key.lower()")
68 68
69 69 does the same as::
70 70
71 71 >>> sys.modules | ifilter("value is not None") | isort("key.lower()")
72 72
73 73 In addition to expression strings, it's possible to pass callables (taking
74 74 the object as an argument) to ``ifilter()``, ``isort()`` and ``ieval()``::
75 75
76 76 >>> sys | ifilter(lambda _:isinstance(_.value, int)) \
77 77 ... | ieval(lambda _: (_.key, hex(_.value))) | idump
78 78 0 |1
79 79 api_version|0x3f4
80 80 dllhandle |0x1e000000
81 81 hexversion |0x20402f0
82 82 maxint |0x7fffffff
83 83 maxunicode |0xffff
84 84 """
85 85
86 86 skip_doctest = True # ignore top-level docstring as a doctest.
87 87
88 88 import sys, os, os.path, stat, glob, new, csv, datetime, types
89 89 import itertools, mimetypes, StringIO
90 90
91 91 try: # Python 2.3 compatibility
92 92 import collections
93 93 except ImportError:
94 94 deque = list
95 95 else:
96 96 deque = collections.deque
97 97
98 98 try: # Python 2.3 compatibility
99 99 set
100 100 except NameError:
101 101 import sets
102 102 set = sets.Set
103 103
104 104 try: # Python 2.3 compatibility
105 105 sorted
106 106 except NameError:
107 107 def sorted(iterator, key=None, reverse=False):
108 108 items = list(iterator)
109 109 if key is not None:
110 110 items.sort(lambda i1, i2: cmp(key(i1), key(i2)))
111 111 else:
112 112 items.sort()
113 113 if reverse:
114 114 items.reverse()
115 115 return items
116 116
117 117 try: # Python 2.4 compatibility
118 118 GeneratorExit
119 119 except NameError:
120 120 GeneratorExit = SystemExit
121 121
122 122 try:
123 123 import pwd
124 124 except ImportError:
125 125 pwd = None
126 126
127 127 try:
128 128 import grp
129 129 except ImportError:
130 130 grp = None
131 131
132 132 from IPython.external import simplegeneric
133 133 from IPython.external import path
134 134
135 135 try:
136 136 from IPython.utils import genutils
137 137 from IPython.utils import generics
138 138 except ImportError:
139 139 genutils = None
140 140 generics = None
141 141
142 from IPython import ipapi
142 from IPython.core import ipapi
143 143
144 144
145 145 __all__ = [
146 146 "ifile", "ils", "iglob", "iwalk", "ipwdentry", "ipwd", "igrpentry", "igrp",
147 147 "icsv", "ix", "ichain", "isort", "ifilter", "ieval", "ienum",
148 148 "ienv", "ihist", "ialias", "icap", "idump", "iless"
149 149 ]
150 150
151 151
152 152 os.stat_float_times(True) # enable microseconds
153 153
154 154
155 155 class AttrNamespace(object):
156 156 """
157 157 Helper class that is used for providing a namespace for evaluating
158 158 expressions containing attribute names of an object.
159 159 """
160 160 def __init__(self, wrapped):
161 161 self.wrapped = wrapped
162 162
163 163 def __getitem__(self, name):
164 164 if name == "_":
165 165 return self.wrapped
166 166 try:
167 167 return getattr(self.wrapped, name)
168 168 except AttributeError:
169 169 raise KeyError(name)
170 170
171 171 # Python 2.3 compatibility
172 172 # use eval workaround to find out which names are used in the
173 173 # eval string and put them into the locals. This works for most
174 174 # normal uses case, bizarre ones like accessing the locals()
175 175 # will fail
176 176 try:
177 177 eval("_", None, AttrNamespace(None))
178 178 except TypeError:
179 179 real_eval = eval
180 180 def eval(codestring, _globals, _locals):
181 181 """
182 182 eval(source[, globals[, locals]]) -> value
183 183
184 184 Evaluate the source in the context of globals and locals.
185 185 The source may be a string representing a Python expression
186 186 or a code object as returned by compile().
187 187 The globals must be a dictionary and locals can be any mappping.
188 188
189 189 This function is a workaround for the shortcomings of
190 190 Python 2.3's eval.
191 191 """
192 192
193 193 if isinstance(codestring, basestring):
194 194 code = compile(codestring, "_eval", "eval")
195 195 else:
196 196 code = codestring
197 197 newlocals = {}
198 198 for name in code.co_names:
199 199 try:
200 200 newlocals[name] = _locals[name]
201 201 except KeyError:
202 202 pass
203 203 return real_eval(code, _globals, newlocals)
204 204
205 205
206 206 noitem = object()
207 207
208 208
209 209 def item(iterator, index, default=noitem):
210 210 """
211 211 Return the ``index``th item from the iterator ``iterator``.
212 212 ``index`` must be an integer (negative integers are relative to the
213 213 end (i.e. the last items produced by the iterator)).
214 214
215 215 If ``default`` is given, this will be the default value when
216 216 the iterator doesn't contain an item at this position. Otherwise an
217 217 ``IndexError`` will be raised.
218 218
219 219 Note that using this function will partially or totally exhaust the
220 220 iterator.
221 221 """
222 222 i = index
223 223 if i>=0:
224 224 for item in iterator:
225 225 if not i:
226 226 return item
227 227 i -= 1
228 228 else:
229 229 i = -index
230 230 cache = deque()
231 231 for item in iterator:
232 232 cache.append(item)
233 233 if len(cache)>i:
234 234 cache.popleft()
235 235 if len(cache)==i:
236 236 return cache.popleft()
237 237 if default is noitem:
238 238 raise IndexError(index)
239 239 else:
240 240 return default
241 241
242 242
243 243 def getglobals(g):
244 244 """
245 245 Return the global namespace that is used for expression strings in
246 246 ``ifilter`` and others. This is ``g`` or (if ``g`` is ``None``) IPython's
247 247 user namespace.
248 248 """
249 249 if g is None:
250 250 if ipapi is not None:
251 251 api = ipapi.get()
252 252 if api is not None:
253 253 return api.user_ns
254 254 return globals()
255 255 return g
256 256
257 257
258 258 class Descriptor(object):
259 259 """
260 260 A ``Descriptor`` object is used for describing the attributes of objects.
261 261 """
262 262 def __hash__(self):
263 263 return hash(self.__class__) ^ hash(self.key())
264 264
265 265 def __eq__(self, other):
266 266 return self.__class__ is other.__class__ and self.key() == other.key()
267 267
268 268 def __ne__(self, other):
269 269 return self.__class__ is not other.__class__ or self.key() != other.key()
270 270
271 271 def key(self):
272 272 pass
273 273
274 274 def name(self):
275 275 """
276 276 Return the name of this attribute for display by a ``Display`` object
277 277 (e.g. as a column title).
278 278 """
279 279 key = self.key()
280 280 if key is None:
281 281 return "_"
282 282 return str(key)
283 283
284 284 def attrtype(self, obj):
285 285 """
286 286 Return the type of this attribute (i.e. something like "attribute" or
287 287 "method").
288 288 """
289 289
290 290 def valuetype(self, obj):
291 291 """
292 292 Return the type of this attribute value of the object ``obj``.
293 293 """
294 294
295 295 def value(self, obj):
296 296 """
297 297 Return the value of this attribute of the object ``obj``.
298 298 """
299 299
300 300 def doc(self, obj):
301 301 """
302 302 Return the documentation for this attribute.
303 303 """
304 304
305 305 def shortdoc(self, obj):
306 306 """
307 307 Return a short documentation for this attribute (defaulting to the
308 308 first line).
309 309 """
310 310 doc = self.doc(obj)
311 311 if doc is not None:
312 312 doc = doc.strip().splitlines()[0].strip()
313 313 return doc
314 314
315 315 def iter(self, obj):
316 316 """
317 317 Return an iterator for this attribute of the object ``obj``.
318 318 """
319 319 return xiter(self.value(obj))
320 320
321 321
322 322 class SelfDescriptor(Descriptor):
323 323 """
324 324 A ``SelfDescriptor`` describes the object itself.
325 325 """
326 326 def key(self):
327 327 return None
328 328
329 329 def attrtype(self, obj):
330 330 return "self"
331 331
332 332 def valuetype(self, obj):
333 333 return type(obj)
334 334
335 335 def value(self, obj):
336 336 return obj
337 337
338 338 def __repr__(self):
339 339 return "Self"
340 340
341 341 selfdescriptor = SelfDescriptor() # there's no need for more than one
342 342
343 343
344 344 class AttributeDescriptor(Descriptor):
345 345 """
346 346 An ``AttributeDescriptor`` describes a simple attribute of an object.
347 347 """
348 348 __slots__ = ("_name", "_doc")
349 349
350 350 def __init__(self, name, doc=None):
351 351 self._name = name
352 352 self._doc = doc
353 353
354 354 def key(self):
355 355 return self._name
356 356
357 357 def doc(self, obj):
358 358 return self._doc
359 359
360 360 def attrtype(self, obj):
361 361 return "attr"
362 362
363 363 def valuetype(self, obj):
364 364 return type(getattr(obj, self._name))
365 365
366 366 def value(self, obj):
367 367 return getattr(obj, self._name)
368 368
369 369 def __repr__(self):
370 370 if self._doc is None:
371 371 return "Attribute(%r)" % self._name
372 372 else:
373 373 return "Attribute(%r, %r)" % (self._name, self._doc)
374 374
375 375
376 376 class IndexDescriptor(Descriptor):
377 377 """
378 378 An ``IndexDescriptor`` describes an "attribute" of an object that is fetched
379 379 via ``__getitem__``.
380 380 """
381 381 __slots__ = ("_index",)
382 382
383 383 def __init__(self, index):
384 384 self._index = index
385 385
386 386 def key(self):
387 387 return self._index
388 388
389 389 def attrtype(self, obj):
390 390 return "item"
391 391
392 392 def valuetype(self, obj):
393 393 return type(obj[self._index])
394 394
395 395 def value(self, obj):
396 396 return obj[self._index]
397 397
398 398 def __repr__(self):
399 399 return "Index(%r)" % self._index
400 400
401 401
402 402 class MethodDescriptor(Descriptor):
403 403 """
404 404 A ``MethodDescriptor`` describes a method of an object that can be called
405 405 without argument. Note that this method shouldn't change the object.
406 406 """
407 407 __slots__ = ("_name", "_doc")
408 408
409 409 def __init__(self, name, doc=None):
410 410 self._name = name
411 411 self._doc = doc
412 412
413 413 def key(self):
414 414 return self._name
415 415
416 416 def doc(self, obj):
417 417 if self._doc is None:
418 418 return getattr(obj, self._name).__doc__
419 419 return self._doc
420 420
421 421 def attrtype(self, obj):
422 422 return "method"
423 423
424 424 def valuetype(self, obj):
425 425 return type(self.value(obj))
426 426
427 427 def value(self, obj):
428 428 return getattr(obj, self._name)()
429 429
430 430 def __repr__(self):
431 431 if self._doc is None:
432 432 return "Method(%r)" % self._name
433 433 else:
434 434 return "Method(%r, %r)" % (self._name, self._doc)
435 435
436 436
437 437 class IterAttributeDescriptor(Descriptor):
438 438 """
439 439 An ``IterAttributeDescriptor`` works like an ``AttributeDescriptor`` but
440 440 doesn't return an attribute values (because this value might be e.g. a large
441 441 list).
442 442 """
443 443 __slots__ = ("_name", "_doc")
444 444
445 445 def __init__(self, name, doc=None):
446 446 self._name = name
447 447 self._doc = doc
448 448
449 449 def key(self):
450 450 return self._name
451 451
452 452 def doc(self, obj):
453 453 return self._doc
454 454
455 455 def attrtype(self, obj):
456 456 return "iter"
457 457
458 458 def valuetype(self, obj):
459 459 return noitem
460 460
461 461 def value(self, obj):
462 462 return noitem
463 463
464 464 def iter(self, obj):
465 465 return xiter(getattr(obj, self._name))
466 466
467 467 def __repr__(self):
468 468 if self._doc is None:
469 469 return "IterAttribute(%r)" % self._name
470 470 else:
471 471 return "IterAttribute(%r, %r)" % (self._name, self._doc)
472 472
473 473
474 474 class IterMethodDescriptor(Descriptor):
475 475 """
476 476 An ``IterMethodDescriptor`` works like an ``MethodDescriptor`` but doesn't
477 477 return an attribute values (because this value might be e.g. a large list).
478 478 """
479 479 __slots__ = ("_name", "_doc")
480 480
481 481 def __init__(self, name, doc=None):
482 482 self._name = name
483 483 self._doc = doc
484 484
485 485 def key(self):
486 486 return self._name
487 487
488 488 def doc(self, obj):
489 489 if self._doc is None:
490 490 return getattr(obj, self._name).__doc__
491 491 return self._doc
492 492
493 493 def attrtype(self, obj):
494 494 return "itermethod"
495 495
496 496 def valuetype(self, obj):
497 497 return noitem
498 498
499 499 def value(self, obj):
500 500 return noitem
501 501
502 502 def iter(self, obj):
503 503 return xiter(getattr(obj, self._name)())
504 504
505 505 def __repr__(self):
506 506 if self._doc is None:
507 507 return "IterMethod(%r)" % self._name
508 508 else:
509 509 return "IterMethod(%r, %r)" % (self._name, self._doc)
510 510
511 511
512 512 class FunctionDescriptor(Descriptor):
513 513 """
514 514 A ``FunctionDescriptor`` turns a function into a descriptor. The function
515 515 will be called with the object to get the type and value of the attribute.
516 516 """
517 517 __slots__ = ("_function", "_name", "_doc")
518 518
519 519 def __init__(self, function, name=None, doc=None):
520 520 self._function = function
521 521 self._name = name
522 522 self._doc = doc
523 523
524 524 def key(self):
525 525 return self._function
526 526
527 527 def name(self):
528 528 if self._name is not None:
529 529 return self._name
530 530 return getattr(self._function, "__xname__", self._function.__name__)
531 531
532 532 def doc(self, obj):
533 533 if self._doc is None:
534 534 return self._function.__doc__
535 535 return self._doc
536 536
537 537 def attrtype(self, obj):
538 538 return "function"
539 539
540 540 def valuetype(self, obj):
541 541 return type(self._function(obj))
542 542
543 543 def value(self, obj):
544 544 return self._function(obj)
545 545
546 546 def __repr__(self):
547 547 if self._doc is None:
548 548 return "Function(%r)" % self._name
549 549 else:
550 550 return "Function(%r, %r)" % (self._name, self._doc)
551 551
552 552
553 553 class Table(object):
554 554 """
555 555 A ``Table`` is an object that produces items (just like a normal Python
556 556 iterator/generator does) and can be used as the first object in a pipeline
557 557 expression. The displayhook will open the default browser for such an object
558 558 (instead of simply printing the ``repr()`` result).
559 559 """
560 560
561 561 # We want to support ``foo`` and ``foo()`` in pipeline expression:
562 562 # So we implement the required operators (``|`` and ``+``) in the metaclass,
563 563 # instantiate the class and forward the operator to the instance
564 564 class __metaclass__(type):
565 565 def __iter__(self):
566 566 return iter(self())
567 567
568 568 def __or__(self, other):
569 569 return self() | other
570 570
571 571 def __add__(self, other):
572 572 return self() + other
573 573
574 574 def __radd__(self, other):
575 575 return other + self()
576 576
577 577 def __getitem__(self, index):
578 578 return self()[index]
579 579
580 580 def __getitem__(self, index):
581 581 return item(self, index)
582 582
583 583 def __contains__(self, item):
584 584 for haveitem in self:
585 585 if item == haveitem:
586 586 return True
587 587 return False
588 588
589 589 def __or__(self, other):
590 590 # autoinstantiate right hand side
591 591 if isinstance(other, type) and issubclass(other, (Table, Display)):
592 592 other = other()
593 593 # treat simple strings and functions as ``ieval`` instances
594 594 elif not isinstance(other, Display) and not isinstance(other, Table):
595 595 other = ieval(other)
596 596 # forward operations to the right hand side
597 597 return other.__ror__(self)
598 598
599 599 def __add__(self, other):
600 600 # autoinstantiate right hand side
601 601 if isinstance(other, type) and issubclass(other, Table):
602 602 other = other()
603 603 return ichain(self, other)
604 604
605 605 def __radd__(self, other):
606 606 # autoinstantiate left hand side
607 607 if isinstance(other, type) and issubclass(other, Table):
608 608 other = other()
609 609 return ichain(other, self)
610 610
611 611
612 612 class Pipe(Table):
613 613 """
614 614 A ``Pipe`` is an object that can be used in a pipeline expression. It
615 615 processes the objects it gets from its input ``Table``/``Pipe``. Note that
616 616 a ``Pipe`` object can't be used as the first object in a pipeline
617 617 expression, as it doesn't produces items itself.
618 618 """
619 619 class __metaclass__(Table.__metaclass__):
620 620 def __ror__(self, input):
621 621 return input | self()
622 622
623 623 def __ror__(self, input):
624 624 # autoinstantiate left hand side
625 625 if isinstance(input, type) and issubclass(input, Table):
626 626 input = input()
627 627 self.input = input
628 628 return self
629 629
630 630
631 631 def xrepr(item, mode="default"):
632 632 """
633 633 Generic function that adds color output and different display modes to ``repr``.
634 634
635 635 The result of an ``xrepr`` call is iterable and consists of ``(style, string)``
636 636 tuples. The ``style`` in this tuple must be a ``Style`` object from the
637 637 ``astring`` module. To reconfigure the output the first yielded tuple can be
638 638 a ``(aligment, full)`` tuple instead of a ``(style, string)`` tuple.
639 639 ``alignment`` can be -1 for left aligned, 0 for centered and 1 for right
640 640 aligned (the default is left alignment). ``full`` is a boolean that specifies
641 641 whether the complete output must be displayed or the ``Display`` object is
642 642 allowed to stop output after enough text has been produced (e.g. a syntax
643 643 highlighted text line would use ``True``, but for a large data structure
644 644 (i.e. a nested list, tuple or dictionary) ``False`` would be used).
645 645 The default is full output.
646 646
647 647 There are four different possible values for ``mode`` depending on where
648 648 the ``Display`` object will display ``item``:
649 649
650 650 ``"header"``
651 651 ``item`` will be displayed in a header line (this is used by ``ibrowse``).
652 652
653 653 ``"footer"``
654 654 ``item`` will be displayed in a footer line (this is used by ``ibrowse``).
655 655
656 656 ``"cell"``
657 657 ``item`` will be displayed in a table cell/list.
658 658
659 659 ``"default"``
660 660 default mode. If an ``xrepr`` implementation recursively outputs objects,
661 661 ``"default"`` must be passed in the recursive calls to ``xrepr``.
662 662
663 663 If no implementation is registered for ``item``, ``xrepr`` will try the
664 664 ``__xrepr__`` method on ``item``. If ``item`` doesn't have an ``__xrepr__``
665 665 method it falls back to ``repr``/``__repr__`` for all modes.
666 666 """
667 667 try:
668 668 func = item.__xrepr__
669 669 except AttributeError:
670 670 yield (astyle.style_default, repr(item))
671 671 else:
672 672 try:
673 673 for x in func(mode):
674 674 yield x
675 675 except (KeyboardInterrupt, SystemExit, GeneratorExit):
676 676 raise
677 677 except Exception:
678 678 yield (astyle.style_default, repr(item))
679 679 xrepr = simplegeneric.generic(xrepr)
680 680
681 681
682 682 def xrepr_none(self, mode="default"):
683 683 yield (astyle.style_type_none, repr(self))
684 684 xrepr.when_object(None)(xrepr_none)
685 685
686 686
687 687 def xrepr_noitem(self, mode="default"):
688 688 yield (2, True)
689 689 yield (astyle.style_nodata, "<?>")
690 690 xrepr.when_object(noitem)(xrepr_noitem)
691 691
692 692
693 693 def xrepr_bool(self, mode="default"):
694 694 yield (astyle.style_type_bool, repr(self))
695 695 xrepr.when_type(bool)(xrepr_bool)
696 696
697 697
698 698 def xrepr_str(self, mode="default"):
699 699 if mode == "cell":
700 700 yield (astyle.style_default, repr(self.expandtabs(tab))[1:-1])
701 701 else:
702 702 yield (astyle.style_default, repr(self))
703 703 xrepr.when_type(str)(xrepr_str)
704 704
705 705
706 706 def xrepr_unicode(self, mode="default"):
707 707 if mode == "cell":
708 708 yield (astyle.style_default, repr(self.expandtabs(tab))[2:-1])
709 709 else:
710 710 yield (astyle.style_default, repr(self))
711 711 xrepr.when_type(unicode)(xrepr_unicode)
712 712
713 713
714 714 def xrepr_number(self, mode="default"):
715 715 yield (1, True)
716 716 yield (astyle.style_type_number, repr(self))
717 717 xrepr.when_type(int)(xrepr_number)
718 718 xrepr.when_type(long)(xrepr_number)
719 719 xrepr.when_type(float)(xrepr_number)
720 720
721 721
722 722 def xrepr_complex(self, mode="default"):
723 723 yield (astyle.style_type_number, repr(self))
724 724 xrepr.when_type(complex)(xrepr_number)
725 725
726 726
727 727 def xrepr_datetime(self, mode="default"):
728 728 if mode == "cell":
729 729 # Don't use strftime() here, as this requires year >= 1900
730 730 yield (astyle.style_type_datetime,
731 731 "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \
732 732 (self.year, self.month, self.day,
733 733 self.hour, self.minute, self.second,
734 734 self.microsecond),
735 735 )
736 736 else:
737 737 yield (astyle.style_type_datetime, repr(self))
738 738 xrepr.when_type(datetime.datetime)(xrepr_datetime)
739 739
740 740
741 741 def xrepr_date(self, mode="default"):
742 742 if mode == "cell":
743 743 yield (astyle.style_type_datetime,
744 744 "%04d-%02d-%02d" % (self.year, self.month, self.day))
745 745 else:
746 746 yield (astyle.style_type_datetime, repr(self))
747 747 xrepr.when_type(datetime.date)(xrepr_date)
748 748
749 749
750 750 def xrepr_time(self, mode="default"):
751 751 if mode == "cell":
752 752 yield (astyle.style_type_datetime,
753 753 "%02d:%02d:%02d.%06d" % \
754 754 (self.hour, self.minute, self.second, self.microsecond))
755 755 else:
756 756 yield (astyle.style_type_datetime, repr(self))
757 757 xrepr.when_type(datetime.time)(xrepr_time)
758 758
759 759
760 760 def xrepr_timedelta(self, mode="default"):
761 761 yield (astyle.style_type_datetime, repr(self))
762 762 xrepr.when_type(datetime.timedelta)(xrepr_timedelta)
763 763
764 764
765 765 def xrepr_type(self, mode="default"):
766 766 if self.__module__ == "__builtin__":
767 767 yield (astyle.style_type_type, self.__name__)
768 768 else:
769 769 yield (astyle.style_type_type, "%s.%s" % (self.__module__, self.__name__))
770 770 xrepr.when_type(type)(xrepr_type)
771 771
772 772
773 773 def xrepr_exception(self, mode="default"):
774 774 if self.__class__.__module__ == "exceptions":
775 775 classname = self.__class__.__name__
776 776 else:
777 777 classname = "%s.%s" % \
778 778 (self.__class__.__module__, self.__class__.__name__)
779 779 if mode == "header" or mode == "footer":
780 780 yield (astyle.style_error, "%s: %s" % (classname, self))
781 781 else:
782 782 yield (astyle.style_error, classname)
783 783 xrepr.when_type(Exception)(xrepr_exception)
784 784
785 785
786 786 def xrepr_listtuple(self, mode="default"):
787 787 if mode == "header" or mode == "footer":
788 788 if self.__class__.__module__ == "__builtin__":
789 789 classname = self.__class__.__name__
790 790 else:
791 791 classname = "%s.%s" % \
792 792 (self.__class__.__module__,self.__class__.__name__)
793 793 yield (astyle.style_default,
794 794 "<%s object with %d items at 0x%x>" % \
795 795 (classname, len(self), id(self)))
796 796 else:
797 797 yield (-1, False)
798 798 if isinstance(self, list):
799 799 yield (astyle.style_default, "[")
800 800 end = "]"
801 801 else:
802 802 yield (astyle.style_default, "(")
803 803 end = ")"
804 804 for (i, subself) in enumerate(self):
805 805 if i:
806 806 yield (astyle.style_default, ", ")
807 807 for part in xrepr(subself, "default"):
808 808 yield part
809 809 yield (astyle.style_default, end)
810 810 xrepr.when_type(list)(xrepr_listtuple)
811 811 xrepr.when_type(tuple)(xrepr_listtuple)
812 812
813 813
814 814 def xrepr_dict(self, mode="default"):
815 815 if mode == "header" or mode == "footer":
816 816 if self.__class__.__module__ == "__builtin__":
817 817 classname = self.__class__.__name__
818 818 else:
819 819 classname = "%s.%s" % \
820 820 (self.__class__.__module__,self.__class__.__name__)
821 821 yield (astyle.style_default,
822 822 "<%s object with %d items at 0x%x>" % \
823 823 (classname, len(self), id(self)))
824 824 else:
825 825 yield (-1, False)
826 826 if isinstance(self, dict):
827 827 yield (astyle.style_default, "{")
828 828 end = "}"
829 829 else:
830 830 yield (astyle.style_default, "dictproxy((")
831 831 end = "})"
832 832 for (i, (key, value)) in enumerate(self.iteritems()):
833 833 if i:
834 834 yield (astyle.style_default, ", ")
835 835 for part in xrepr(key, "default"):
836 836 yield part
837 837 yield (astyle.style_default, ": ")
838 838 for part in xrepr(value, "default"):
839 839 yield part
840 840 yield (astyle.style_default, end)
841 841 xrepr.when_type(dict)(xrepr_dict)
842 842 xrepr.when_type(types.DictProxyType)(xrepr_dict)
843 843
844 844
845 845 def upgradexattr(attr):
846 846 """
847 847 Convert an attribute descriptor string to a real descriptor object.
848 848
849 849 If attr already is a descriptor object return it unmodified. A
850 850 ``SelfDescriptor`` will be returned if ``attr`` is ``None``. ``"foo"``
851 851 returns an ``AttributeDescriptor`` for the attribute named ``"foo"``.
852 852 ``"foo()"`` returns a ``MethodDescriptor`` for the method named ``"foo"``.
853 853 ``"-foo"`` will return an ``IterAttributeDescriptor`` for the attribute
854 854 named ``"foo"`` and ``"-foo()"`` will return an ``IterMethodDescriptor``
855 855 for the method named ``"foo"``. Furthermore integers will return the appropriate
856 856 ``IndexDescriptor`` and callables will return a ``FunctionDescriptor``.
857 857 """
858 858 if attr is None:
859 859 return selfdescriptor
860 860 elif isinstance(attr, Descriptor):
861 861 return attr
862 862 elif isinstance(attr, basestring):
863 863 if attr.endswith("()"):
864 864 if attr.startswith("-"):
865 865 return IterMethodDescriptor(attr[1:-2])
866 866 else:
867 867 return MethodDescriptor(attr[:-2])
868 868 else:
869 869 if attr.startswith("-"):
870 870 return IterAttributeDescriptor(attr[1:])
871 871 else:
872 872 return AttributeDescriptor(attr)
873 873 elif isinstance(attr, (int, long)):
874 874 return IndexDescriptor(attr)
875 875 elif callable(attr):
876 876 return FunctionDescriptor(attr)
877 877 else:
878 878 raise TypeError("can't handle descriptor %r" % attr)
879 879
880 880
881 881 def xattrs(item, mode="default"):
882 882 """
883 883 Generic function that returns an iterable of attribute descriptors
884 884 to be used for displaying the attributes ob the object ``item`` in display
885 885 mode ``mode``.
886 886
887 887 There are two possible modes:
888 888
889 889 ``"detail"``
890 890 The ``Display`` object wants to display a detailed list of the object
891 891 attributes.
892 892
893 893 ``"default"``
894 894 The ``Display`` object wants to display the object in a list view.
895 895
896 896 If no implementation is registered for the object ``item`` ``xattrs`` falls
897 897 back to trying the ``__xattrs__`` method of the object. If this doesn't
898 898 exist either, ``dir(item)`` is used for ``"detail"`` mode and ``(None,)``
899 899 for ``"default"`` mode.
900 900
901 901 The implementation must yield attribute descriptors (see the class
902 902 ``Descriptor`` for more info). The ``__xattrs__`` method may also return
903 903 attribute descriptor strings (and ``None``) which will be converted to real
904 904 descriptors by ``upgradexattr()``.
905 905 """
906 906 try:
907 907 func = item.__xattrs__
908 908 except AttributeError:
909 909 if mode == "detail":
910 910 for attrname in dir(item):
911 911 yield AttributeDescriptor(attrname)
912 912 else:
913 913 yield selfdescriptor
914 914 else:
915 915 for attr in func(mode):
916 916 yield upgradexattr(attr)
917 917 xattrs = simplegeneric.generic(xattrs)
918 918
919 919
920 920 def xattrs_complex(self, mode="default"):
921 921 if mode == "detail":
922 922 return (AttributeDescriptor("real"), AttributeDescriptor("imag"))
923 923 return (selfdescriptor,)
924 924 xattrs.when_type(complex)(xattrs_complex)
925 925
926 926
927 927 def _isdict(item):
928 928 try:
929 929 itermeth = item.__class__.__iter__
930 930 except (AttributeError, TypeError):
931 931 return False
932 932 return itermeth is dict.__iter__ or itermeth is types.DictProxyType.__iter__
933 933
934 934
935 935 def _isstr(item):
936 936 if not isinstance(item, basestring):
937 937 return False
938 938 try:
939 939 itermeth = item.__class__.__iter__
940 940 except AttributeError:
941 941 return True
942 942 return False # ``__iter__`` has been redefined
943 943
944 944
945 945 def xiter(item):
946 946 """
947 947 Generic function that implements iteration for pipeline expression. If no
948 948 implementation is registered for ``item`` ``xiter`` falls back to ``iter``.
949 949 """
950 950 try:
951 951 func = item.__xiter__
952 952 except AttributeError:
953 953 if _isdict(item):
954 954 def items(item):
955 955 fields = ("key", "value")
956 956 for (key, value) in item.iteritems():
957 957 yield Fields(fields, key=key, value=value)
958 958 return items(item)
959 959 elif isinstance(item, new.module):
960 960 def items(item):
961 961 fields = ("key", "value")
962 962 for key in sorted(item.__dict__):
963 963 yield Fields(fields, key=key, value=getattr(item, key))
964 964 return items(item)
965 965 elif _isstr(item):
966 966 if not item:
967 967 raise ValueError("can't enter empty string")
968 968 lines = item.splitlines()
969 969 if len(lines) == 1:
970 970 def iterone(item):
971 971 yield item
972 972 return iterone(item)
973 973 else:
974 974 return iter(lines)
975 975 return iter(item)
976 976 else:
977 977 return iter(func()) # iter() just to be safe
978 978 xiter = simplegeneric.generic(xiter)
979 979
980 980
981 981 class ichain(Pipe):
982 982 """
983 983 Chains multiple ``Table``s into one.
984 984 """
985 985
986 986 def __init__(self, *iters):
987 987 self.iters = iters
988 988
989 989 def __iter__(self):
990 990 return itertools.chain(*self.iters)
991 991
992 992 def __xrepr__(self, mode="default"):
993 993 if mode == "header" or mode == "footer":
994 994 for (i, item) in enumerate(self.iters):
995 995 if i:
996 996 yield (astyle.style_default, "+")
997 997 if isinstance(item, Pipe):
998 998 yield (astyle.style_default, "(")
999 999 for part in xrepr(item, mode):
1000 1000 yield part
1001 1001 if isinstance(item, Pipe):
1002 1002 yield (astyle.style_default, ")")
1003 1003 else:
1004 1004 yield (astyle.style_default, repr(self))
1005 1005
1006 1006 def __repr__(self):
1007 1007 args = ", ".join([repr(it) for it in self.iters])
1008 1008 return "%s.%s(%s)" % \
1009 1009 (self.__class__.__module__, self.__class__.__name__, args)
1010 1010
1011 1011
1012 1012 class ifile(path.path):
1013 1013 """
1014 1014 file (or directory) object.
1015 1015 """
1016 1016
1017 1017 def getmode(self):
1018 1018 return self.stat().st_mode
1019 1019 mode = property(getmode, None, None, "Access mode")
1020 1020
1021 1021 def gettype(self):
1022 1022 data = [
1023 1023 (stat.S_ISREG, "file"),
1024 1024 (stat.S_ISDIR, "dir"),
1025 1025 (stat.S_ISCHR, "chardev"),
1026 1026 (stat.S_ISBLK, "blockdev"),
1027 1027 (stat.S_ISFIFO, "fifo"),
1028 1028 (stat.S_ISLNK, "symlink"),
1029 1029 (stat.S_ISSOCK,"socket"),
1030 1030 ]
1031 1031 lstat = self.lstat()
1032 1032 if lstat is not None:
1033 1033 types = set([text for (func, text) in data if func(lstat.st_mode)])
1034 1034 else:
1035 1035 types = set()
1036 1036 m = self.mode
1037 1037 types.update([text for (func, text) in data if func(m)])
1038 1038 return ", ".join(types)
1039 1039 type = property(gettype, None, None, "file type (file, directory, link, etc.)")
1040 1040
1041 1041 def getmodestr(self):
1042 1042 m = self.mode
1043 1043 data = [
1044 1044 (stat.S_IRUSR, "-r"),
1045 1045 (stat.S_IWUSR, "-w"),
1046 1046 (stat.S_IXUSR, "-x"),
1047 1047 (stat.S_IRGRP, "-r"),
1048 1048 (stat.S_IWGRP, "-w"),
1049 1049 (stat.S_IXGRP, "-x"),
1050 1050 (stat.S_IROTH, "-r"),
1051 1051 (stat.S_IWOTH, "-w"),
1052 1052 (stat.S_IXOTH, "-x"),
1053 1053 ]
1054 1054 return "".join([text[bool(m&bit)] for (bit, text) in data])
1055 1055
1056 1056 modestr = property(getmodestr, None, None, "Access mode as string")
1057 1057
1058 1058 def getblocks(self):
1059 1059 return self.stat().st_blocks
1060 1060 blocks = property(getblocks, None, None, "File size in blocks")
1061 1061
1062 1062 def getblksize(self):
1063 1063 return self.stat().st_blksize
1064 1064 blksize = property(getblksize, None, None, "Filesystem block size")
1065 1065
1066 1066 def getdev(self):
1067 1067 return self.stat().st_dev
1068 1068 dev = property(getdev)
1069 1069
1070 1070 def getnlink(self):
1071 1071 return self.stat().st_nlink
1072 1072 nlink = property(getnlink, None, None, "Number of links")
1073 1073
1074 1074 def getuid(self):
1075 1075 return self.stat().st_uid
1076 1076 uid = property(getuid, None, None, "User id of file owner")
1077 1077
1078 1078 def getgid(self):
1079 1079 return self.stat().st_gid
1080 1080 gid = property(getgid, None, None, "Group id of file owner")
1081 1081
1082 1082 def getowner(self):
1083 1083 stat = self.stat()
1084 1084 try:
1085 1085 return pwd.getpwuid(stat.st_uid).pw_name
1086 1086 except KeyError:
1087 1087 return stat.st_uid
1088 1088 owner = property(getowner, None, None, "Owner name (or id)")
1089 1089
1090 1090 def getgroup(self):
1091 1091 stat = self.stat()
1092 1092 try:
1093 1093 return grp.getgrgid(stat.st_gid).gr_name
1094 1094 except KeyError:
1095 1095 return stat.st_gid
1096 1096 group = property(getgroup, None, None, "Group name (or id)")
1097 1097
1098 1098 def getadate(self):
1099 1099 return datetime.datetime.utcfromtimestamp(self.atime)
1100 1100 adate = property(getadate, None, None, "Access date")
1101 1101
1102 1102 def getcdate(self):
1103 1103 return datetime.datetime.utcfromtimestamp(self.ctime)
1104 1104 cdate = property(getcdate, None, None, "Creation date")
1105 1105
1106 1106 def getmdate(self):
1107 1107 return datetime.datetime.utcfromtimestamp(self.mtime)
1108 1108 mdate = property(getmdate, None, None, "Modification date")
1109 1109
1110 1110 def mimetype(self):
1111 1111 """
1112 1112 Return MIME type guessed from the extension.
1113 1113 """
1114 1114 return mimetypes.guess_type(self.basename())[0]
1115 1115
1116 1116 def encoding(self):
1117 1117 """
1118 1118 Return guessed compression (like "compress" or "gzip").
1119 1119 """
1120 1120 return mimetypes.guess_type(self.basename())[1]
1121 1121
1122 1122 def __repr__(self):
1123 1123 return "ifile(%s)" % path._base.__repr__(self)
1124 1124
1125 1125 if sys.platform == "win32":
1126 1126 defaultattrs = (None, "type", "size", "modestr", "mdate")
1127 1127 else:
1128 1128 defaultattrs = (None, "type", "size", "modestr", "owner", "group", "mdate")
1129 1129
1130 1130 def __xattrs__(self, mode="default"):
1131 1131 if mode == "detail":
1132 1132 return (
1133 1133 "name",
1134 1134 "basename()",
1135 1135 "abspath()",
1136 1136 "realpath()",
1137 1137 "type",
1138 1138 "mode",
1139 1139 "modestr",
1140 1140 "stat()",
1141 1141 "lstat()",
1142 1142 "uid",
1143 1143 "gid",
1144 1144 "owner",
1145 1145 "group",
1146 1146 "dev",
1147 1147 "nlink",
1148 1148 "ctime",
1149 1149 "mtime",
1150 1150 "atime",
1151 1151 "cdate",
1152 1152 "mdate",
1153 1153 "adate",
1154 1154 "size",
1155 1155 "blocks",
1156 1156 "blksize",
1157 1157 "isdir()",
1158 1158 "islink()",
1159 1159 "mimetype()",
1160 1160 "encoding()",
1161 1161 "-listdir()",
1162 1162 "-dirs()",
1163 1163 "-files()",
1164 1164 "-walk()",
1165 1165 "-walkdirs()",
1166 1166 "-walkfiles()",
1167 1167 )
1168 1168 else:
1169 1169 return self.defaultattrs
1170 1170
1171 1171
1172 1172 def xiter_ifile(self):
1173 1173 if self.isdir():
1174 1174 yield (self / os.pardir).abspath()
1175 1175 for child in sorted(self.listdir()):
1176 1176 yield child
1177 1177 else:
1178 1178 f = self.open("rb")
1179 1179 for line in f:
1180 1180 yield line
1181 1181 f.close()
1182 1182 xiter.when_type(ifile)(xiter_ifile)
1183 1183
1184 1184
1185 1185 # We need to implement ``xrepr`` for ``ifile`` as a generic function, because
1186 1186 # otherwise ``xrepr_str`` would kick in.
1187 1187 def xrepr_ifile(self, mode="default"):
1188 1188 try:
1189 1189 if self.isdir():
1190 1190 name = "idir"
1191 1191 style = astyle.style_dir
1192 1192 else:
1193 1193 name = "ifile"
1194 1194 style = astyle.style_file
1195 1195 except IOError:
1196 1196 name = "ifile"
1197 1197 style = astyle.style_default
1198 1198 if mode in ("cell", "header", "footer"):
1199 1199 abspath = repr(path._base(self.normpath()))
1200 1200 if abspath.startswith("u"):
1201 1201 abspath = abspath[2:-1]
1202 1202 else:
1203 1203 abspath = abspath[1:-1]
1204 1204 if mode == "cell":
1205 1205 yield (style, abspath)
1206 1206 else:
1207 1207 yield (style, "%s(%s)" % (name, abspath))
1208 1208 else:
1209 1209 yield (style, repr(self))
1210 1210 xrepr.when_type(ifile)(xrepr_ifile)
1211 1211
1212 1212
1213 1213 class ils(Table):
1214 1214 """
1215 1215 List the current (or a specified) directory.
1216 1216
1217 1217 Examples::
1218 1218
1219 1219 >>> ils
1220 1220 <class 'IPython.Extensions.ipipe.ils'>
1221 1221 >>> ils("/usr/local/lib/python2.4")
1222 1222 IPython.Extensions.ipipe.ils('/usr/local/lib/python2.4')
1223 1223 >>> ils("~")
1224 1224 IPython.Extensions.ipipe.ils('/home/fperez')
1225 1225 # all-random
1226 1226 """
1227 1227 def __init__(self, base=os.curdir, dirs=True, files=True):
1228 1228 self.base = os.path.expanduser(base)
1229 1229 self.dirs = dirs
1230 1230 self.files = files
1231 1231
1232 1232 def __iter__(self):
1233 1233 base = ifile(self.base)
1234 1234 yield (base / os.pardir).abspath()
1235 1235 for child in sorted(base.listdir()):
1236 1236 if self.dirs:
1237 1237 if self.files:
1238 1238 yield child
1239 1239 else:
1240 1240 if child.isdir():
1241 1241 yield child
1242 1242 elif self.files:
1243 1243 if not child.isdir():
1244 1244 yield child
1245 1245
1246 1246 def __xrepr__(self, mode="default"):
1247 1247 return xrepr(ifile(self.base), mode)
1248 1248
1249 1249 def __repr__(self):
1250 1250 return "%s.%s(%r)" % \
1251 1251 (self.__class__.__module__, self.__class__.__name__, self.base)
1252 1252
1253 1253
1254 1254 class iglob(Table):
1255 1255 """
1256 1256 List all files and directories matching a specified pattern.
1257 1257 (See ``glob.glob()`` for more info.).
1258 1258
1259 1259 Examples::
1260 1260
1261 1261 >>> iglob("*.py")
1262 1262 IPython.Extensions.ipipe.iglob('*.py')
1263 1263 """
1264 1264 def __init__(self, glob):
1265 1265 self.glob = glob
1266 1266
1267 1267 def __iter__(self):
1268 1268 for name in glob.glob(self.glob):
1269 1269 yield ifile(name)
1270 1270
1271 1271 def __xrepr__(self, mode="default"):
1272 1272 if mode == "header" or mode == "footer" or mode == "cell":
1273 1273 yield (astyle.style_default,
1274 1274 "%s(%r)" % (self.__class__.__name__, self.glob))
1275 1275 else:
1276 1276 yield (astyle.style_default, repr(self))
1277 1277
1278 1278 def __repr__(self):
1279 1279 return "%s.%s(%r)" % \
1280 1280 (self.__class__.__module__, self.__class__.__name__, self.glob)
1281 1281
1282 1282
1283 1283 class iwalk(Table):
1284 1284 """
1285 1285 List all files and directories in a directory and it's subdirectory::
1286 1286
1287 1287 >>> iwalk
1288 1288 <class 'IPython.Extensions.ipipe.iwalk'>
1289 1289 >>> iwalk("/usr/lib")
1290 1290 IPython.Extensions.ipipe.iwalk('/usr/lib')
1291 1291 >>> iwalk("~")
1292 1292 IPython.Extensions.ipipe.iwalk('/home/fperez') # random
1293 1293
1294 1294 """
1295 1295 def __init__(self, base=os.curdir, dirs=True, files=True):
1296 1296 self.base = os.path.expanduser(base)
1297 1297 self.dirs = dirs
1298 1298 self.files = files
1299 1299
1300 1300 def __iter__(self):
1301 1301 for (dirpath, dirnames, filenames) in os.walk(self.base):
1302 1302 if self.dirs:
1303 1303 for name in sorted(dirnames):
1304 1304 yield ifile(os.path.join(dirpath, name))
1305 1305 if self.files:
1306 1306 for name in sorted(filenames):
1307 1307 yield ifile(os.path.join(dirpath, name))
1308 1308
1309 1309 def __xrepr__(self, mode="default"):
1310 1310 if mode == "header" or mode == "footer" or mode == "cell":
1311 1311 yield (astyle.style_default,
1312 1312 "%s(%r)" % (self.__class__.__name__, self.base))
1313 1313 else:
1314 1314 yield (astyle.style_default, repr(self))
1315 1315
1316 1316 def __repr__(self):
1317 1317 return "%s.%s(%r)" % \
1318 1318 (self.__class__.__module__, self.__class__.__name__, self.base)
1319 1319
1320 1320
1321 1321 class ipwdentry(object):
1322 1322 """
1323 1323 ``ipwdentry`` objects encapsulate entries in the Unix user account and
1324 1324 password database.
1325 1325 """
1326 1326 def __init__(self, id):
1327 1327 self._id = id
1328 1328 self._entry = None
1329 1329
1330 1330 def __eq__(self, other):
1331 1331 return self.__class__ is other.__class__ and self._id == other._id
1332 1332
1333 1333 def __ne__(self, other):
1334 1334 return self.__class__ is not other.__class__ or self._id != other._id
1335 1335
1336 1336 def _getentry(self):
1337 1337 if self._entry is None:
1338 1338 if isinstance(self._id, basestring):
1339 1339 self._entry = pwd.getpwnam(self._id)
1340 1340 else:
1341 1341 self._entry = pwd.getpwuid(self._id)
1342 1342 return self._entry
1343 1343
1344 1344 def getname(self):
1345 1345 if isinstance(self._id, basestring):
1346 1346 return self._id
1347 1347 else:
1348 1348 return self._getentry().pw_name
1349 1349 name = property(getname, None, None, "User name")
1350 1350
1351 1351 def getpasswd(self):
1352 1352 return self._getentry().pw_passwd
1353 1353 passwd = property(getpasswd, None, None, "Password")
1354 1354
1355 1355 def getuid(self):
1356 1356 if isinstance(self._id, basestring):
1357 1357 return self._getentry().pw_uid
1358 1358 else:
1359 1359 return self._id
1360 1360 uid = property(getuid, None, None, "User id")
1361 1361
1362 1362 def getgid(self):
1363 1363 return self._getentry().pw_gid
1364 1364 gid = property(getgid, None, None, "Primary group id")
1365 1365
1366 1366 def getgroup(self):
1367 1367 return igrpentry(self.gid)
1368 1368 group = property(getgroup, None, None, "Group")
1369 1369
1370 1370 def getgecos(self):
1371 1371 return self._getentry().pw_gecos
1372 1372 gecos = property(getgecos, None, None, "Information (e.g. full user name)")
1373 1373
1374 1374 def getdir(self):
1375 1375 return self._getentry().pw_dir
1376 1376 dir = property(getdir, None, None, "$HOME directory")
1377 1377
1378 1378 def getshell(self):
1379 1379 return self._getentry().pw_shell
1380 1380 shell = property(getshell, None, None, "Login shell")
1381 1381
1382 1382 def __xattrs__(self, mode="default"):
1383 1383 return ("name", "passwd", "uid", "gid", "gecos", "dir", "shell")
1384 1384
1385 1385 def __repr__(self):
1386 1386 return "%s.%s(%r)" % \
1387 1387 (self.__class__.__module__, self.__class__.__name__, self._id)
1388 1388
1389 1389
1390 1390 class ipwd(Table):
1391 1391 """
1392 1392 List all entries in the Unix user account and password database.
1393 1393
1394 1394 Example::
1395 1395
1396 1396 >>> ipwd | isort("uid")
1397 1397 <IPython.Extensions.ipipe.isort key='uid' reverse=False at 0x849efec>
1398 1398 # random
1399 1399 """
1400 1400 def __iter__(self):
1401 1401 for entry in pwd.getpwall():
1402 1402 yield ipwdentry(entry.pw_name)
1403 1403
1404 1404 def __xrepr__(self, mode="default"):
1405 1405 if mode == "header" or mode == "footer" or mode == "cell":
1406 1406 yield (astyle.style_default, "%s()" % self.__class__.__name__)
1407 1407 else:
1408 1408 yield (astyle.style_default, repr(self))
1409 1409
1410 1410
1411 1411 class igrpentry(object):
1412 1412 """
1413 1413 ``igrpentry`` objects encapsulate entries in the Unix group database.
1414 1414 """
1415 1415 def __init__(self, id):
1416 1416 self._id = id
1417 1417 self._entry = None
1418 1418
1419 1419 def __eq__(self, other):
1420 1420 return self.__class__ is other.__class__ and self._id == other._id
1421 1421
1422 1422 def __ne__(self, other):
1423 1423 return self.__class__ is not other.__class__ or self._id != other._id
1424 1424
1425 1425 def _getentry(self):
1426 1426 if self._entry is None:
1427 1427 if isinstance(self._id, basestring):
1428 1428 self._entry = grp.getgrnam(self._id)
1429 1429 else:
1430 1430 self._entry = grp.getgrgid(self._id)
1431 1431 return self._entry
1432 1432
1433 1433 def getname(self):
1434 1434 if isinstance(self._id, basestring):
1435 1435 return self._id
1436 1436 else:
1437 1437 return self._getentry().gr_name
1438 1438 name = property(getname, None, None, "Group name")
1439 1439
1440 1440 def getpasswd(self):
1441 1441 return self._getentry().gr_passwd
1442 1442 passwd = property(getpasswd, None, None, "Password")
1443 1443
1444 1444 def getgid(self):
1445 1445 if isinstance(self._id, basestring):
1446 1446 return self._getentry().gr_gid
1447 1447 else:
1448 1448 return self._id
1449 1449 gid = property(getgid, None, None, "Group id")
1450 1450
1451 1451 def getmem(self):
1452 1452 return self._getentry().gr_mem
1453 1453 mem = property(getmem, None, None, "Members")
1454 1454
1455 1455 def __xattrs__(self, mode="default"):
1456 1456 return ("name", "passwd", "gid", "mem")
1457 1457
1458 1458 def __xrepr__(self, mode="default"):
1459 1459 if mode == "header" or mode == "footer" or mode == "cell":
1460 1460 yield (astyle.style_default, "group ")
1461 1461 try:
1462 1462 yield (astyle.style_default, self.name)
1463 1463 except KeyError:
1464 1464 if isinstance(self._id, basestring):
1465 1465 yield (astyle.style_default, self.name_id)
1466 1466 else:
1467 1467 yield (astyle.style_type_number, str(self._id))
1468 1468 else:
1469 1469 yield (astyle.style_default, repr(self))
1470 1470
1471 1471 def __iter__(self):
1472 1472 for member in self.mem:
1473 1473 yield ipwdentry(member)
1474 1474
1475 1475 def __repr__(self):
1476 1476 return "%s.%s(%r)" % \
1477 1477 (self.__class__.__module__, self.__class__.__name__, self._id)
1478 1478
1479 1479
1480 1480 class igrp(Table):
1481 1481 """
1482 1482 This ``Table`` lists all entries in the Unix group database.
1483 1483 """
1484 1484 def __iter__(self):
1485 1485 for entry in grp.getgrall():
1486 1486 yield igrpentry(entry.gr_name)
1487 1487
1488 1488 def __xrepr__(self, mode="default"):
1489 1489 if mode == "header" or mode == "footer":
1490 1490 yield (astyle.style_default, "%s()" % self.__class__.__name__)
1491 1491 else:
1492 1492 yield (astyle.style_default, repr(self))
1493 1493
1494 1494
1495 1495 class Fields(object):
1496 1496 def __init__(self, fieldnames, **fields):
1497 1497 self.__fieldnames = [upgradexattr(fieldname) for fieldname in fieldnames]
1498 1498 for (key, value) in fields.iteritems():
1499 1499 setattr(self, key, value)
1500 1500
1501 1501 def __xattrs__(self, mode="default"):
1502 1502 return self.__fieldnames
1503 1503
1504 1504 def __xrepr__(self, mode="default"):
1505 1505 yield (-1, False)
1506 1506 if mode == "header" or mode == "cell":
1507 1507 yield (astyle.style_default, self.__class__.__name__)
1508 1508 yield (astyle.style_default, "(")
1509 1509 for (i, f) in enumerate(self.__fieldnames):
1510 1510 if i:
1511 1511 yield (astyle.style_default, ", ")
1512 1512 yield (astyle.style_default, f.name())
1513 1513 yield (astyle.style_default, "=")
1514 1514 for part in xrepr(getattr(self, f), "default"):
1515 1515 yield part
1516 1516 yield (astyle.style_default, ")")
1517 1517 elif mode == "footer":
1518 1518 yield (astyle.style_default, self.__class__.__name__)
1519 1519 yield (astyle.style_default, "(")
1520 1520 for (i, f) in enumerate(self.__fieldnames):
1521 1521 if i:
1522 1522 yield (astyle.style_default, ", ")
1523 1523 yield (astyle.style_default, f.name())
1524 1524 yield (astyle.style_default, ")")
1525 1525 else:
1526 1526 yield (astyle.style_default, repr(self))
1527 1527
1528 1528
1529 1529 class FieldTable(Table, list):
1530 1530 def __init__(self, *fields):
1531 1531 Table.__init__(self)
1532 1532 list.__init__(self)
1533 1533 self.fields = fields
1534 1534
1535 1535 def add(self, **fields):
1536 1536 self.append(Fields(self.fields, **fields))
1537 1537
1538 1538 def __xrepr__(self, mode="default"):
1539 1539 yield (-1, False)
1540 1540 if mode == "header" or mode == "footer":
1541 1541 yield (astyle.style_default, self.__class__.__name__)
1542 1542 yield (astyle.style_default, "(")
1543 1543 for (i, f) in enumerate(self.__fieldnames):
1544 1544 if i:
1545 1545 yield (astyle.style_default, ", ")
1546 1546 yield (astyle.style_default, f)
1547 1547 yield (astyle.style_default, ")")
1548 1548 else:
1549 1549 yield (astyle.style_default, repr(self))
1550 1550
1551 1551 def __repr__(self):
1552 1552 return "<%s.%s object with fields=%r at 0x%x>" % \
1553 1553 (self.__class__.__module__, self.__class__.__name__,
1554 1554 ", ".join(map(repr, self.fields)), id(self))
1555 1555
1556 1556
1557 1557 class List(list):
1558 1558 def __xattrs__(self, mode="default"):
1559 1559 return xrange(len(self))
1560 1560
1561 1561 def __xrepr__(self, mode="default"):
1562 1562 yield (-1, False)
1563 1563 if mode == "header" or mode == "cell" or mode == "footer" or mode == "default":
1564 1564 yield (astyle.style_default, self.__class__.__name__)
1565 1565 yield (astyle.style_default, "(")
1566 1566 for (i, item) in enumerate(self):
1567 1567 if i:
1568 1568 yield (astyle.style_default, ", ")
1569 1569 for part in xrepr(item, "default"):
1570 1570 yield part
1571 1571 yield (astyle.style_default, ")")
1572 1572 else:
1573 1573 yield (astyle.style_default, repr(self))
1574 1574
1575 1575
1576 1576 class ienv(Table):
1577 1577 """
1578 1578 List environment variables.
1579 1579
1580 1580 Example::
1581 1581
1582 1582 >>> ienv
1583 1583 <class 'IPython.Extensions.ipipe.ienv'>
1584 1584 """
1585 1585
1586 1586 def __iter__(self):
1587 1587 fields = ("key", "value")
1588 1588 for (key, value) in os.environ.iteritems():
1589 1589 yield Fields(fields, key=key, value=value)
1590 1590
1591 1591 def __xrepr__(self, mode="default"):
1592 1592 if mode == "header" or mode == "cell":
1593 1593 yield (astyle.style_default, "%s()" % self.__class__.__name__)
1594 1594 else:
1595 1595 yield (astyle.style_default, repr(self))
1596 1596
1597 1597
1598 1598 class ihist(Table):
1599 1599 """
1600 1600 IPython input history
1601 1601
1602 1602 Example::
1603 1603
1604 1604 >>> ihist
1605 1605 <class 'IPython.Extensions.ipipe.ihist'>
1606 1606 >>> ihist(True) # raw mode
1607 1607 <IPython.Extensions.ipipe.ihist object at 0x849602c> # random
1608 1608 """
1609 1609 def __init__(self, raw=True):
1610 1610 self.raw = raw
1611 1611
1612 1612 def __iter__(self):
1613 1613 api = ipapi.get()
1614 1614 if self.raw:
1615 1615 for line in api.IP.input_hist_raw:
1616 1616 yield line.rstrip("\n")
1617 1617 else:
1618 1618 for line in api.IP.input_hist:
1619 1619 yield line.rstrip("\n")
1620 1620
1621 1621
1622 1622 class Alias(object):
1623 1623 """
1624 1624 Entry in the alias table
1625 1625 """
1626 1626 def __init__(self, name, args, command):
1627 1627 self.name = name
1628 1628 self.args = args
1629 1629 self.command = command
1630 1630
1631 1631 def __xattrs__(self, mode="default"):
1632 1632 return ("name", "args", "command")
1633 1633
1634 1634
1635 1635 class ialias(Table):
1636 1636 """
1637 1637 IPython alias list
1638 1638
1639 1639 Example::
1640 1640
1641 1641 >>> ialias
1642 1642 <class 'IPython.Extensions.ipipe.ialias'>
1643 1643 """
1644 1644 def __iter__(self):
1645 1645 api = ipapi.get()
1646 1646
1647 1647 for (name, (args, command)) in api.IP.alias_table.iteritems():
1648 1648 yield Alias(name, args, command)
1649 1649
1650 1650
1651 1651 class icsv(Pipe):
1652 1652 """
1653 1653 This ``Pipe`` turns the input (with must be a pipe outputting lines
1654 1654 or an ``ifile``) into lines of CVS columns.
1655 1655 """
1656 1656 def __init__(self, **csvargs):
1657 1657 """
1658 1658 Create an ``icsv`` object. ``cvsargs`` will be passed through as
1659 1659 keyword arguments to ``cvs.reader()``.
1660 1660 """
1661 1661 self.csvargs = csvargs
1662 1662
1663 1663 def __iter__(self):
1664 1664 input = self.input
1665 1665 if isinstance(input, ifile):
1666 1666 input = input.open("rb")
1667 1667 reader = csv.reader(input, **self.csvargs)
1668 1668 for line in reader:
1669 1669 yield List(line)
1670 1670
1671 1671 def __xrepr__(self, mode="default"):
1672 1672 yield (-1, False)
1673 1673 if mode == "header" or mode == "footer":
1674 1674 input = getattr(self, "input", None)
1675 1675 if input is not None:
1676 1676 for part in xrepr(input, mode):
1677 1677 yield part
1678 1678 yield (astyle.style_default, " | ")
1679 1679 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1680 1680 for (i, (name, value)) in enumerate(self.csvargs.iteritems()):
1681 1681 if i:
1682 1682 yield (astyle.style_default, ", ")
1683 1683 yield (astyle.style_default, name)
1684 1684 yield (astyle.style_default, "=")
1685 1685 for part in xrepr(value, "default"):
1686 1686 yield part
1687 1687 yield (astyle.style_default, ")")
1688 1688 else:
1689 1689 yield (astyle.style_default, repr(self))
1690 1690
1691 1691 def __repr__(self):
1692 1692 args = ", ".join(["%s=%r" % item for item in self.csvargs.iteritems()])
1693 1693 return "<%s.%s %s at 0x%x>" % \
1694 1694 (self.__class__.__module__, self.__class__.__name__, args, id(self))
1695 1695
1696 1696
1697 1697 class ix(Table):
1698 1698 """
1699 1699 Execute a system command and list its output as lines
1700 1700 (similar to ``os.popen()``).
1701 1701
1702 1702 Examples::
1703 1703
1704 1704 >>> ix("ps x")
1705 1705 IPython.Extensions.ipipe.ix('ps x')
1706 1706
1707 1707 >>> ix("find .") | ifile
1708 1708 <IPython.Extensions.ipipe.ieval expr=<class 'IPython.Extensions.ipipe.ifile'> at 0x8509d2c>
1709 1709 # random
1710 1710 """
1711 1711 def __init__(self, cmd):
1712 1712 self.cmd = cmd
1713 1713 self._pipeout = None
1714 1714
1715 1715 def __iter__(self):
1716 1716 (_pipein, self._pipeout) = os.popen4(self.cmd)
1717 1717 _pipein.close()
1718 1718 for l in self._pipeout:
1719 1719 yield l.rstrip("\r\n")
1720 1720 self._pipeout.close()
1721 1721 self._pipeout = None
1722 1722
1723 1723 def __del__(self):
1724 1724 if self._pipeout is not None and not self._pipeout.closed:
1725 1725 self._pipeout.close()
1726 1726 self._pipeout = None
1727 1727
1728 1728 def __xrepr__(self, mode="default"):
1729 1729 if mode == "header" or mode == "footer":
1730 1730 yield (astyle.style_default,
1731 1731 "%s(%r)" % (self.__class__.__name__, self.cmd))
1732 1732 else:
1733 1733 yield (astyle.style_default, repr(self))
1734 1734
1735 1735 def __repr__(self):
1736 1736 return "%s.%s(%r)" % \
1737 1737 (self.__class__.__module__, self.__class__.__name__, self.cmd)
1738 1738
1739 1739
1740 1740 class ifilter(Pipe):
1741 1741 """
1742 1742 Filter an input pipe. Only objects where an expression evaluates to true
1743 1743 (and doesn't raise an exception) are listed.
1744 1744
1745 1745 Examples::
1746 1746
1747 1747 >>> ils | ifilter("_.isfile() and size>1000")
1748 1748 >>> igrp | ifilter("len(mem)")
1749 1749 >>> sys.modules | ifilter(lambda _:_.value is not None)
1750 1750 # all-random
1751 1751 """
1752 1752
1753 1753 def __init__(self, expr, globals=None, errors="raiseifallfail"):
1754 1754 """
1755 1755 Create an ``ifilter`` object. ``expr`` can be a callable or a string
1756 1756 containing an expression. ``globals`` will be used as the global
1757 1757 namespace for calling string expressions (defaulting to IPython's
1758 1758 user namespace). ``errors`` specifies how exception during evaluation
1759 1759 of ``expr`` are handled:
1760 1760
1761 1761 ``"drop"``
1762 1762 drop all items that have errors;
1763 1763
1764 1764 ``"keep"``
1765 1765 keep all items that have errors;
1766 1766
1767 1767 ``"keeperror"``
1768 1768 keep the exception of all items that have errors;
1769 1769
1770 1770 ``"raise"``
1771 1771 raise the exception;
1772 1772
1773 1773 ``"raiseifallfail"``
1774 1774 raise the first exception if all items have errors; otherwise drop
1775 1775 those with errors (this is the default).
1776 1776 """
1777 1777 self.expr = expr
1778 1778 self.globals = globals
1779 1779 self.errors = errors
1780 1780
1781 1781 def __iter__(self):
1782 1782 if callable(self.expr):
1783 1783 test = self.expr
1784 1784 else:
1785 1785 g = getglobals(self.globals)
1786 1786 expr = compile(self.expr, "ipipe-expression", "eval")
1787 1787 def test(item):
1788 1788 return eval(expr, g, AttrNamespace(item))
1789 1789
1790 1790 ok = 0
1791 1791 exc_info = None
1792 1792 for item in xiter(self.input):
1793 1793 try:
1794 1794 if test(item):
1795 1795 yield item
1796 1796 ok += 1
1797 1797 except (KeyboardInterrupt, SystemExit):
1798 1798 raise
1799 1799 except Exception, exc:
1800 1800 if self.errors == "drop":
1801 1801 pass # Ignore errors
1802 1802 elif self.errors == "keep":
1803 1803 yield item
1804 1804 elif self.errors == "keeperror":
1805 1805 yield exc
1806 1806 elif self.errors == "raise":
1807 1807 raise
1808 1808 elif self.errors == "raiseifallfail":
1809 1809 if exc_info is None:
1810 1810 exc_info = sys.exc_info()
1811 1811 if not ok and exc_info is not None:
1812 1812 raise exc_info[0], exc_info[1], exc_info[2]
1813 1813
1814 1814 def __xrepr__(self, mode="default"):
1815 1815 if mode == "header" or mode == "footer":
1816 1816 input = getattr(self, "input", None)
1817 1817 if input is not None:
1818 1818 for part in xrepr(input, mode):
1819 1819 yield part
1820 1820 yield (astyle.style_default, " | ")
1821 1821 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1822 1822 for part in xrepr(self.expr, "default"):
1823 1823 yield part
1824 1824 yield (astyle.style_default, ")")
1825 1825 else:
1826 1826 yield (astyle.style_default, repr(self))
1827 1827
1828 1828 def __repr__(self):
1829 1829 return "<%s.%s expr=%r at 0x%x>" % \
1830 1830 (self.__class__.__module__, self.__class__.__name__,
1831 1831 self.expr, id(self))
1832 1832
1833 1833
1834 1834 class ieval(Pipe):
1835 1835 """
1836 1836 Evaluate an expression for each object in the input pipe.
1837 1837
1838 1838 Examples::
1839 1839
1840 1840 >>> ils | ieval("_.abspath()")
1841 1841 # random
1842 1842 >>> sys.path | ieval(ifile)
1843 1843 # random
1844 1844 """
1845 1845
1846 1846 def __init__(self, expr, globals=None, errors="raiseifallfail"):
1847 1847 """
1848 1848 Create an ``ieval`` object. ``expr`` can be a callable or a string
1849 1849 containing an expression. For the meaning of ``globals`` and
1850 1850 ``errors`` see ``ifilter``.
1851 1851 """
1852 1852 self.expr = expr
1853 1853 self.globals = globals
1854 1854 self.errors = errors
1855 1855
1856 1856 def __iter__(self):
1857 1857 if callable(self.expr):
1858 1858 do = self.expr
1859 1859 else:
1860 1860 g = getglobals(self.globals)
1861 1861 expr = compile(self.expr, "ipipe-expression", "eval")
1862 1862 def do(item):
1863 1863 return eval(expr, g, AttrNamespace(item))
1864 1864
1865 1865 ok = 0
1866 1866 exc_info = None
1867 1867 for item in xiter(self.input):
1868 1868 try:
1869 1869 yield do(item)
1870 1870 except (KeyboardInterrupt, SystemExit):
1871 1871 raise
1872 1872 except Exception, exc:
1873 1873 if self.errors == "drop":
1874 1874 pass # Ignore errors
1875 1875 elif self.errors == "keep":
1876 1876 yield item
1877 1877 elif self.errors == "keeperror":
1878 1878 yield exc
1879 1879 elif self.errors == "raise":
1880 1880 raise
1881 1881 elif self.errors == "raiseifallfail":
1882 1882 if exc_info is None:
1883 1883 exc_info = sys.exc_info()
1884 1884 if not ok and exc_info is not None:
1885 1885 raise exc_info[0], exc_info[1], exc_info[2]
1886 1886
1887 1887 def __xrepr__(self, mode="default"):
1888 1888 if mode == "header" or mode == "footer":
1889 1889 input = getattr(self, "input", None)
1890 1890 if input is not None:
1891 1891 for part in xrepr(input, mode):
1892 1892 yield part
1893 1893 yield (astyle.style_default, " | ")
1894 1894 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1895 1895 for part in xrepr(self.expr, "default"):
1896 1896 yield part
1897 1897 yield (astyle.style_default, ")")
1898 1898 else:
1899 1899 yield (astyle.style_default, repr(self))
1900 1900
1901 1901 def __repr__(self):
1902 1902 return "<%s.%s expr=%r at 0x%x>" % \
1903 1903 (self.__class__.__module__, self.__class__.__name__,
1904 1904 self.expr, id(self))
1905 1905
1906 1906
1907 1907 class ienum(Pipe):
1908 1908 """
1909 1909 Enumerate the input pipe (i.e. wrap each input object in an object
1910 1910 with ``index`` and ``object`` attributes).
1911 1911
1912 1912 Examples::
1913 1913
1914 1914 >>> xrange(20) | ieval("_,_*_") | ienum | ifilter("index % 2 == 0") | ieval("object")
1915 1915 """
1916 1916 skip_doctest = True
1917 1917
1918 1918 def __iter__(self):
1919 1919 fields = ("index", "object")
1920 1920 for (index, object) in enumerate(xiter(self.input)):
1921 1921 yield Fields(fields, index=index, object=object)
1922 1922
1923 1923
1924 1924 class isort(Pipe):
1925 1925 """
1926 1926 Sorts the input pipe.
1927 1927
1928 1928 Examples::
1929 1929
1930 1930 >>> ils | isort("size")
1931 1931 <IPython.Extensions.ipipe.isort key='size' reverse=False at 0x849ec2c>
1932 1932 >>> ils | isort("_.isdir(), _.lower()", reverse=True)
1933 1933 <IPython.Extensions.ipipe.isort key='_.isdir(), _.lower()' reverse=True at 0x849eacc>
1934 1934 # all-random
1935 1935 """
1936 1936
1937 1937 def __init__(self, key=None, globals=None, reverse=False):
1938 1938 """
1939 1939 Create an ``isort`` object. ``key`` can be a callable or a string
1940 1940 containing an expression (or ``None`` in which case the items
1941 1941 themselves will be sorted). If ``reverse`` is true the sort order
1942 1942 will be reversed. For the meaning of ``globals`` see ``ifilter``.
1943 1943 """
1944 1944 self.key = key
1945 1945 self.globals = globals
1946 1946 self.reverse = reverse
1947 1947
1948 1948 def __iter__(self):
1949 1949 if self.key is None:
1950 1950 items = sorted(xiter(self.input), reverse=self.reverse)
1951 1951 elif callable(self.key):
1952 1952 items = sorted(xiter(self.input), key=self.key, reverse=self.reverse)
1953 1953 else:
1954 1954 g = getglobals(self.globals)
1955 1955 key = compile(self.key, "ipipe-expression", "eval")
1956 1956 def realkey(item):
1957 1957 return eval(key, g, AttrNamespace(item))
1958 1958 items = sorted(xiter(self.input), key=realkey, reverse=self.reverse)
1959 1959 for item in items:
1960 1960 yield item
1961 1961
1962 1962 def __xrepr__(self, mode="default"):
1963 1963 if mode == "header" or mode == "footer":
1964 1964 input = getattr(self, "input", None)
1965 1965 if input is not None:
1966 1966 for part in xrepr(input, mode):
1967 1967 yield part
1968 1968 yield (astyle.style_default, " | ")
1969 1969 yield (astyle.style_default, "%s(" % self.__class__.__name__)
1970 1970 for part in xrepr(self.key, "default"):
1971 1971 yield part
1972 1972 if self.reverse:
1973 1973 yield (astyle.style_default, ", ")
1974 1974 for part in xrepr(True, "default"):
1975 1975 yield part
1976 1976 yield (astyle.style_default, ")")
1977 1977 else:
1978 1978 yield (astyle.style_default, repr(self))
1979 1979
1980 1980 def __repr__(self):
1981 1981 return "<%s.%s key=%r reverse=%r at 0x%x>" % \
1982 1982 (self.__class__.__module__, self.__class__.__name__,
1983 1983 self.key, self.reverse, id(self))
1984 1984
1985 1985
1986 1986 tab = 3 # for expandtabs()
1987 1987
1988 1988 def _format(field):
1989 1989 if isinstance(field, str):
1990 1990 text = repr(field.expandtabs(tab))[1:-1]
1991 1991 elif isinstance(field, unicode):
1992 1992 text = repr(field.expandtabs(tab))[2:-1]
1993 1993 elif isinstance(field, datetime.datetime):
1994 1994 # Don't use strftime() here, as this requires year >= 1900
1995 1995 text = "%04d-%02d-%02d %02d:%02d:%02d.%06d" % \
1996 1996 (field.year, field.month, field.day,
1997 1997 field.hour, field.minute, field.second, field.microsecond)
1998 1998 elif isinstance(field, datetime.date):
1999 1999 text = "%04d-%02d-%02d" % (field.year, field.month, field.day)
2000 2000 else:
2001 2001 text = repr(field)
2002 2002 return text
2003 2003
2004 2004
2005 2005 class Display(object):
2006 2006 class __metaclass__(type):
2007 2007 def __ror__(self, input):
2008 2008 return input | self()
2009 2009
2010 2010 def __init__(self, input=None):
2011 2011 self.input = input
2012 2012
2013 2013 def __ror__(self, input):
2014 2014 self.input = input
2015 2015 return self
2016 2016
2017 2017 def display(self):
2018 2018 pass
2019 2019
2020 2020
2021 2021 class iless(Display):
2022 2022 cmd = "less --quit-if-one-screen --LONG-PROMPT --LINE-NUMBERS --chop-long-lines --shift=8 --RAW-CONTROL-CHARS"
2023 2023
2024 2024 def display(self):
2025 2025 try:
2026 2026 pager = os.popen(self.cmd, "w")
2027 2027 try:
2028 2028 for item in xiter(self.input):
2029 2029 first = False
2030 2030 for attr in xattrs(item, "default"):
2031 2031 if first:
2032 2032 first = False
2033 2033 else:
2034 2034 pager.write(" ")
2035 2035 attr = upgradexattr(attr)
2036 2036 if not isinstance(attr, SelfDescriptor):
2037 2037 pager.write(attr.name())
2038 2038 pager.write("=")
2039 2039 pager.write(str(attr.value(item)))
2040 2040 pager.write("\n")
2041 2041 finally:
2042 2042 pager.close()
2043 2043 except Exception, exc:
2044 2044 print "%s: %s" % (exc.__class__.__name__, str(exc))
2045 2045
2046 2046
2047 2047 class _RedirectIO(object):
2048 2048 def __init__(self,*args,**kwargs):
2049 2049 """
2050 2050 Map the system output streams to self.
2051 2051 """
2052 2052 self.stream = StringIO.StringIO()
2053 2053 self.stdout = sys.stdout
2054 2054 sys.stdout = self
2055 2055 self.stderr = sys.stderr
2056 2056 sys.stderr = self
2057 2057
2058 2058 def write(self, text):
2059 2059 """
2060 2060 Write both to screen and to self.
2061 2061 """
2062 2062 self.stream.write(text)
2063 2063 self.stdout.write(text)
2064 2064 if "\n" in text:
2065 2065 self.stdout.flush()
2066 2066
2067 2067 def writelines(self, lines):
2068 2068 """
2069 2069 Write lines both to screen and to self.
2070 2070 """
2071 2071 self.stream.writelines(lines)
2072 2072 self.stdout.writelines(lines)
2073 2073 self.stdout.flush()
2074 2074
2075 2075 def restore(self):
2076 2076 """
2077 2077 Restore the default system streams.
2078 2078 """
2079 2079 self.stdout.flush()
2080 2080 self.stderr.flush()
2081 2081 sys.stdout = self.stdout
2082 2082 sys.stderr = self.stderr
2083 2083
2084 2084
2085 2085 class icap(Table):
2086 2086 """
2087 2087 Execute a python string and capture any output to stderr/stdout.
2088 2088
2089 2089 Examples::
2090 2090
2091 2091 >>> import time
2092 2092 >>> icap("for i in range(10): print i, time.sleep(0.1)")
2093 2093
2094 2094 """
2095 2095 skip_doctest = True
2096 2096
2097 2097 def __init__(self, expr, globals=None):
2098 2098 self.expr = expr
2099 2099 self.globals = globals
2100 2100 log = _RedirectIO()
2101 2101 try:
2102 2102 exec(expr, getglobals(globals))
2103 2103 finally:
2104 2104 log.restore()
2105 2105 self.stream = log.stream
2106 2106
2107 2107 def __iter__(self):
2108 2108 self.stream.seek(0)
2109 2109 for line in self.stream:
2110 2110 yield line.rstrip("\r\n")
2111 2111
2112 2112 def __xrepr__(self, mode="default"):
2113 2113 if mode == "header" or mode == "footer":
2114 2114 yield (astyle.style_default,
2115 2115 "%s(%r)" % (self.__class__.__name__, self.expr))
2116 2116 else:
2117 2117 yield (astyle.style_default, repr(self))
2118 2118
2119 2119 def __repr__(self):
2120 2120 return "%s.%s(%r)" % \
2121 2121 (self.__class__.__module__, self.__class__.__name__, self.expr)
2122 2122
2123 2123
2124 2124 def xformat(value, mode, maxlength):
2125 2125 align = None
2126 2126 full = True
2127 2127 width = 0
2128 2128 text = astyle.Text()
2129 2129 for (style, part) in xrepr(value, mode):
2130 2130 # only consider the first result
2131 2131 if align is None:
2132 2132 if isinstance(style, int):
2133 2133 # (style, text) really is (alignment, stop)
2134 2134 align = style
2135 2135 full = part
2136 2136 continue
2137 2137 else:
2138 2138 align = -1
2139 2139 full = True
2140 2140 if not isinstance(style, int):
2141 2141 text.append((style, part))
2142 2142 width += len(part)
2143 2143 if width >= maxlength and not full:
2144 2144 text.append((astyle.style_ellisis, "..."))
2145 2145 width += 3
2146 2146 break
2147 2147 if align is None: # default to left alignment
2148 2148 align = -1
2149 2149 return (align, width, text)
2150 2150
2151 2151
2152 2152
2153 2153 import astyle
2154 2154
2155 2155 class idump(Display):
2156 2156 # The approximate maximum length of a column entry
2157 2157 maxattrlength = 200
2158 2158
2159 2159 # Style for column names
2160 2160 style_header = astyle.Style.fromstr("white:black:bold")
2161 2161
2162 2162 def __init__(self, input=None, *attrs):
2163 2163 Display.__init__(self, input)
2164 2164 self.attrs = [upgradexattr(attr) for attr in attrs]
2165 2165 self.headerpadchar = " "
2166 2166 self.headersepchar = "|"
2167 2167 self.datapadchar = " "
2168 2168 self.datasepchar = "|"
2169 2169
2170 2170 def display(self):
2171 2171 stream = genutils.Term.cout
2172 2172 allattrs = []
2173 2173 attrset = set()
2174 2174 colwidths = {}
2175 2175 rows = []
2176 2176 for item in xiter(self.input):
2177 2177 row = {}
2178 2178 attrs = self.attrs
2179 2179 if not attrs:
2180 2180 attrs = xattrs(item, "default")
2181 2181 for attr in attrs:
2182 2182 if attr not in attrset:
2183 2183 allattrs.append(attr)
2184 2184 attrset.add(attr)
2185 2185 colwidths[attr] = len(attr.name())
2186 2186 try:
2187 2187 value = attr.value(item)
2188 2188 except (KeyboardInterrupt, SystemExit):
2189 2189 raise
2190 2190 except Exception, exc:
2191 2191 value = exc
2192 2192 (align, width, text) = xformat(value, "cell", self.maxattrlength)
2193 2193 colwidths[attr] = max(colwidths[attr], width)
2194 2194 # remember alignment, length and colored parts
2195 2195 row[attr] = (align, width, text)
2196 2196 rows.append(row)
2197 2197
2198 2198 stream.write("\n")
2199 2199 for (i, attr) in enumerate(allattrs):
2200 2200 attrname = attr.name()
2201 2201 self.style_header(attrname).write(stream)
2202 2202 spc = colwidths[attr] - len(attrname)
2203 2203 if i < len(colwidths)-1:
2204 2204 stream.write(self.headerpadchar*spc)
2205 2205 stream.write(self.headersepchar)
2206 2206 stream.write("\n")
2207 2207
2208 2208 for row in rows:
2209 2209 for (i, attr) in enumerate(allattrs):
2210 2210 (align, width, text) = row[attr]
2211 2211 spc = colwidths[attr] - width
2212 2212 if align == -1:
2213 2213 text.write(stream)
2214 2214 if i < len(colwidths)-1:
2215 2215 stream.write(self.datapadchar*spc)
2216 2216 elif align == 0:
2217 2217 spc = colwidths[attr] - width
2218 2218 spc1 = spc//2
2219 2219 spc2 = spc-spc1
2220 2220 stream.write(self.datapadchar*spc1)
2221 2221 text.write(stream)
2222 2222 if i < len(colwidths)-1:
2223 2223 stream.write(self.datapadchar*spc2)
2224 2224 else:
2225 2225 stream.write(self.datapadchar*spc)
2226 2226 text.write(stream)
2227 2227 if i < len(colwidths)-1:
2228 2228 stream.write(self.datasepchar)
2229 2229 stream.write("\n")
2230 2230
2231 2231
2232 2232 class AttributeDetail(Table):
2233 2233 """
2234 2234 ``AttributeDetail`` objects are use for displaying a detailed list of object
2235 2235 attributes.
2236 2236 """
2237 2237 def __init__(self, object, descriptor):
2238 2238 self.object = object
2239 2239 self.descriptor = descriptor
2240 2240
2241 2241 def __iter__(self):
2242 2242 return self.descriptor.iter(self.object)
2243 2243
2244 2244 def name(self):
2245 2245 return self.descriptor.name()
2246 2246
2247 2247 def attrtype(self):
2248 2248 return self.descriptor.attrtype(self.object)
2249 2249
2250 2250 def valuetype(self):
2251 2251 return self.descriptor.valuetype(self.object)
2252 2252
2253 2253 def doc(self):
2254 2254 return self.descriptor.doc(self.object)
2255 2255
2256 2256 def shortdoc(self):
2257 2257 return self.descriptor.shortdoc(self.object)
2258 2258
2259 2259 def value(self):
2260 2260 return self.descriptor.value(self.object)
2261 2261
2262 2262 def __xattrs__(self, mode="default"):
2263 2263 attrs = ("name()", "attrtype()", "valuetype()", "value()", "shortdoc()")
2264 2264 if mode == "detail":
2265 2265 attrs += ("doc()",)
2266 2266 return attrs
2267 2267
2268 2268 def __xrepr__(self, mode="default"):
2269 2269 yield (-1, True)
2270 2270 valuetype = self.valuetype()
2271 2271 if valuetype is not noitem:
2272 2272 for part in xrepr(valuetype):
2273 2273 yield part
2274 2274 yield (astyle.style_default, " ")
2275 2275 yield (astyle.style_default, self.attrtype())
2276 2276 yield (astyle.style_default, " ")
2277 2277 yield (astyle.style_default, self.name())
2278 2278 yield (astyle.style_default, " of ")
2279 2279 for part in xrepr(self.object):
2280 2280 yield part
2281 2281
2282 2282
2283 2283 try:
2284 2284 from ibrowse import ibrowse
2285 2285 except ImportError:
2286 2286 # No curses (probably Windows) => try igrid
2287 2287 try:
2288 2288 from igrid import igrid
2289 2289 except ImportError:
2290 2290 # no wx either => use ``idump`` as the default display.
2291 2291 defaultdisplay = idump
2292 2292 else:
2293 2293 defaultdisplay = igrid
2294 2294 __all__.append("igrid")
2295 2295 else:
2296 2296 defaultdisplay = ibrowse
2297 2297 __all__.append("ibrowse")
2298 2298
2299 2299
2300 2300 # If we're running under IPython, register our objects with IPython's
2301 2301 # generic function ``result_display``, else install a displayhook
2302 2302 # directly as sys.displayhook
2303 2303 if generics is not None:
2304 2304 def display_display(obj):
2305 2305 return obj.display()
2306 2306 generics.result_display.when_type(Display)(display_display)
2307 2307
2308 2308 def display_tableobject(obj):
2309 2309 return display_display(defaultdisplay(obj))
2310 2310 generics.result_display.when_type(Table)(display_tableobject)
2311 2311
2312 2312 def display_tableclass(obj):
2313 2313 return display_tableobject(obj())
2314 2314 generics.result_display.when_type(Table.__metaclass__)(display_tableclass)
2315 2315 else:
2316 2316 def installdisplayhook():
2317 2317 _originalhook = sys.displayhook
2318 2318 def displayhook(obj):
2319 2319 if isinstance(obj, type) and issubclass(obj, Table):
2320 2320 obj = obj()
2321 2321 if isinstance(obj, Table):
2322 2322 obj = defaultdisplay(obj)
2323 2323 if isinstance(obj, Display):
2324 2324 return obj.display()
2325 2325 else:
2326 2326 _originalhook(obj)
2327 2327 sys.displayhook = displayhook
2328 2328 installdisplayhook()
@@ -1,19 +1,19 b''
1 1 """ Install various IPython completers
2 2
3 3 IPython extension that installs the completers related to external apps.
4 4
5 5 The actual implementations are in Extensions/ipy_completers.py
6 6
7 7 """
8 import IPython.ipapi
8 from IPython.core import ipapi
9 9
10 ip = IPython.ipapi.get()
10 ip = ipapi.get()
11 11
12 12 from ipy_completers import *
13 13
14 14 ip.set_hook('complete_command', apt_completer, re_key = '.*apt-get')
15 15 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
16 16 ip.set_hook('complete_command', hg_completer, str_key = 'hg')
17 17
18 18 # the old bzr completer is deprecated, we recommend ipy_bzr
19 19 #ip.set_hook('complete_command', bzr_completer, str_key = 'bzr')
@@ -1,340 +1,340 b''
1 1 """
2 2 IPython extension: autoreload modules before executing the next line
3 3
4 4 Try::
5 5
6 6 %autoreload?
7 7
8 8 for documentation.
9 9 """
10 10
11 11 # Pauli Virtanen <pav@iki.fi>, 2008.
12 12 # Thomas Heller, 2000.
13 13 #
14 14 # This IPython module is written by Pauli Virtanen, based on the autoreload
15 15 # code by Thomas Heller.
16 16
17 17 #------------------------------------------------------------------------------
18 18 # Autoreload functionality
19 19 #------------------------------------------------------------------------------
20 20
21 21 import time, os, threading, sys, types, imp, inspect, traceback, atexit
22 22 import weakref
23 23
24 24 def _get_compiled_ext():
25 25 """Official way to get the extension of compiled files (.pyc or .pyo)"""
26 26 for ext, mode, typ in imp.get_suffixes():
27 27 if typ == imp.PY_COMPILED:
28 28 return ext
29 29
30 30 PY_COMPILED_EXT = _get_compiled_ext()
31 31
32 32 class ModuleReloader(object):
33 33 failed = {}
34 34 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
35 35
36 36 modules = {}
37 37 """Modules specially marked as autoreloadable."""
38 38
39 39 skip_modules = {}
40 40 """Modules specially marked as not autoreloadable."""
41 41
42 42 check_all = True
43 43 """Autoreload all modules, not just those listed in 'modules'"""
44 44
45 45 old_objects = {}
46 46 """(module-name, name) -> weakref, for replacing old code objects"""
47 47
48 48 def check(self, check_all=False):
49 49 """Check whether some modules need to be reloaded."""
50 50
51 51 if check_all or self.check_all:
52 52 modules = sys.modules.keys()
53 53 else:
54 54 modules = self.modules.keys()
55 55
56 56 for modname in modules:
57 57 m = sys.modules.get(modname, None)
58 58
59 59 if modname in self.skip_modules:
60 60 continue
61 61
62 62 if not hasattr(m, '__file__'):
63 63 continue
64 64
65 65 if m.__name__ == '__main__':
66 66 # we cannot reload(__main__)
67 67 continue
68 68
69 69 filename = m.__file__
70 70 dirname = os.path.dirname(filename)
71 71 path, ext = os.path.splitext(filename)
72 72
73 73 if ext.lower() == '.py':
74 74 ext = PY_COMPILED_EXT
75 75 filename = os.path.join(dirname, path + PY_COMPILED_EXT)
76 76
77 77 if ext != PY_COMPILED_EXT:
78 78 continue
79 79
80 80 try:
81 81 pymtime = os.stat(filename[:-1]).st_mtime
82 82 if pymtime <= os.stat(filename).st_mtime:
83 83 continue
84 84 if self.failed.get(filename[:-1], None) == pymtime:
85 85 continue
86 86 except OSError:
87 87 continue
88 88
89 89 try:
90 90 superreload(m, reload, self.old_objects)
91 91 if filename[:-1] in self.failed:
92 92 del self.failed[filename[:-1]]
93 93 except:
94 94 print >> sys.stderr, "[autoreload of %s failed: %s]" % (
95 95 modname, traceback.format_exc(1))
96 96 self.failed[filename[:-1]] = pymtime
97 97
98 98 #------------------------------------------------------------------------------
99 99 # superreload
100 100 #------------------------------------------------------------------------------
101 101
102 102 def update_function(old, new):
103 103 """Upgrade the code object of a function"""
104 104 for name in ['func_code', 'func_defaults', 'func_doc',
105 105 'func_closure', 'func_globals', 'func_dict']:
106 106 try:
107 107 setattr(old, name, getattr(new, name))
108 108 except (AttributeError, TypeError):
109 109 pass
110 110
111 111 def update_class(old, new):
112 112 """Replace stuff in the __dict__ of a class, and upgrade
113 113 method code objects"""
114 114 for key in old.__dict__.keys():
115 115 old_obj = getattr(old, key)
116 116
117 117 try:
118 118 new_obj = getattr(new, key)
119 119 except AttributeError:
120 120 # obsolete attribute: remove it
121 121 try:
122 122 delattr(old, key)
123 123 except (AttributeError, TypeError):
124 124 pass
125 125 continue
126 126
127 127 if update_generic(old_obj, new_obj): continue
128 128
129 129 try:
130 130 setattr(old, key, getattr(new, key))
131 131 except (AttributeError, TypeError):
132 132 pass # skip non-writable attributes
133 133
134 134 def update_property(old, new):
135 135 """Replace get/set/del functions of a property"""
136 136 update_generic(old.fdel, new.fdel)
137 137 update_generic(old.fget, new.fget)
138 138 update_generic(old.fset, new.fset)
139 139
140 140 def isinstance2(a, b, typ):
141 141 return isinstance(a, typ) and isinstance(b, typ)
142 142
143 143 UPDATE_RULES = [
144 144 (lambda a, b: isinstance2(a, b, types.ClassType),
145 145 update_class),
146 146 (lambda a, b: isinstance2(a, b, types.TypeType),
147 147 update_class),
148 148 (lambda a, b: isinstance2(a, b, types.FunctionType),
149 149 update_function),
150 150 (lambda a, b: isinstance2(a, b, property),
151 151 update_property),
152 152 (lambda a, b: isinstance2(a, b, types.MethodType),
153 153 lambda a, b: update_function(a.im_func, b.im_func)),
154 154 ]
155 155
156 156 def update_generic(a, b):
157 157 for type_check, update in UPDATE_RULES:
158 158 if type_check(a, b):
159 159 update(a, b)
160 160 return True
161 161 return False
162 162
163 163 class StrongRef(object):
164 164 def __init__(self, obj):
165 165 self.obj = obj
166 166 def __call__(self):
167 167 return self.obj
168 168
169 169 def superreload(module, reload=reload, old_objects={}):
170 170 """Enhanced version of the builtin reload function.
171 171
172 172 superreload remembers objects previously in the module, and
173 173
174 174 - upgrades the class dictionary of every old class in the module
175 175 - upgrades the code object of every old function and method
176 176 - clears the module's namespace before reloading
177 177
178 178 """
179 179
180 180 # collect old objects in the module
181 181 for name, obj in module.__dict__.items():
182 182 if not hasattr(obj, '__module__') or obj.__module__ != module.__name__:
183 183 continue
184 184 key = (module.__name__, name)
185 185 try:
186 186 old_objects.setdefault(key, []).append(weakref.ref(obj))
187 187 except TypeError:
188 188 # weakref doesn't work for all types;
189 189 # create strong references for 'important' cases
190 190 if isinstance(obj, types.ClassType):
191 191 old_objects.setdefault(key, []).append(StrongRef(obj))
192 192
193 193 # reload module
194 194 try:
195 195 # clear namespace first from old cruft
196 196 old_name = module.__name__
197 197 module.__dict__.clear()
198 198 module.__dict__['__name__'] = old_name
199 199 except (TypeError, AttributeError, KeyError):
200 200 pass
201 201 module = reload(module)
202 202
203 203 # iterate over all objects and update functions & classes
204 204 for name, new_obj in module.__dict__.items():
205 205 key = (module.__name__, name)
206 206 if key not in old_objects: continue
207 207
208 208 new_refs = []
209 209 for old_ref in old_objects[key]:
210 210 old_obj = old_ref()
211 211 if old_obj is None: continue
212 212 new_refs.append(old_ref)
213 213 update_generic(old_obj, new_obj)
214 214
215 215 if new_refs:
216 216 old_objects[key] = new_refs
217 217 else:
218 218 del old_objects[key]
219 219
220 220 return module
221 221
222 222 reloader = ModuleReloader()
223 223
224 224 #------------------------------------------------------------------------------
225 225 # IPython connectivity
226 226 #------------------------------------------------------------------------------
227 import IPython.ipapi
227 from IPython.core import ipapi
228 228
229 ip = IPython.ipapi.get()
229 ip = ipapi.get()
230 230
231 231 autoreload_enabled = False
232 232
233 233 def runcode_hook(self):
234 234 if not autoreload_enabled:
235 raise IPython.ipapi.TryNext
235 raise ipapi.TryNext
236 236 try:
237 237 reloader.check()
238 238 except:
239 239 pass
240 240
241 241 def enable_autoreload():
242 242 global autoreload_enabled
243 243 autoreload_enabled = True
244 244
245 245 def disable_autoreload():
246 246 global autoreload_enabled
247 247 autoreload_enabled = False
248 248
249 249 def autoreload_f(self, parameter_s=''):
250 250 r""" %autoreload => Reload modules automatically
251 251
252 252 %autoreload
253 253 Reload all modules (except those excluded by %aimport) automatically now.
254 254
255 255 %autoreload 1
256 256 Reload all modules imported with %aimport every time before executing
257 257 the Python code typed.
258 258
259 259 %autoreload 2
260 260 Reload all modules (except those excluded by %aimport) every time
261 261 before executing the Python code typed.
262 262
263 263 Reloading Python modules in a reliable way is in general difficult,
264 264 and unexpected things may occur. %autoreload tries to work
265 265 around common pitfalls by replacing code objects of functions
266 266 previously in the module with new versions. This makes the following
267 267 things to work:
268 268
269 269 - Functions and classes imported via 'from xxx import foo' are upgraded
270 270 to new versions when 'xxx' is reloaded.
271 271 - Methods and properties of classes are upgraded on reload, so that
272 272 calling 'c.foo()' on an object 'c' created before the reload causes
273 273 the new code for 'foo' to be executed.
274 274
275 275 Some of the known remaining caveats are:
276 276
277 277 - Replacing code objects does not always succeed: changing a @property
278 278 in a class to an ordinary method or a method to a member variable
279 279 can cause problems (but in old objects only).
280 280 - Functions that are removed (eg. via monkey-patching) from a module
281 281 before it is reloaded are not upgraded.
282 282 - C extension modules cannot be reloaded, and so cannot be
283 283 autoreloaded.
284 284
285 285 """
286 286 if parameter_s == '':
287 287 reloader.check(True)
288 288 elif parameter_s == '0':
289 289 disable_autoreload()
290 290 elif parameter_s == '1':
291 291 reloader.check_all = False
292 292 enable_autoreload()
293 293 elif parameter_s == '2':
294 294 reloader.check_all = True
295 295 enable_autoreload()
296 296
297 297 def aimport_f(self, parameter_s=''):
298 298 """%aimport => Import modules for automatic reloading.
299 299
300 300 %aimport
301 301 List modules to automatically import and not to import.
302 302
303 303 %aimport foo
304 304 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
305 305
306 306 %aimport -foo
307 307 Mark module 'foo' to not be autoreloaded for %autoreload 1
308 308
309 309 """
310 310
311 311 modname = parameter_s
312 312 if not modname:
313 313 to_reload = reloader.modules.keys()
314 314 to_reload.sort()
315 315 to_skip = reloader.skip_modules.keys()
316 316 to_skip.sort()
317 317 if reloader.check_all:
318 318 print "Modules to reload:\nall-expect-skipped"
319 319 else:
320 320 print "Modules to reload:\n%s" % ' '.join(to_reload)
321 321 print "\nModules to skip:\n%s" % ' '.join(to_skip)
322 322 elif modname.startswith('-'):
323 323 modname = modname[1:]
324 324 try: del reloader.modules[modname]
325 325 except KeyError: pass
326 326 reloader.skip_modules[modname] = True
327 327 else:
328 328 try: del reloader.skip_modules[modname]
329 329 except KeyError: pass
330 330 reloader.modules[modname] = True
331 331
332 332 mod = __import__(modname)
333 333 ip.to_user_ns({modname: mod})
334 334
335 335 def init():
336 336 ip.expose_magic('autoreload', autoreload_f)
337 337 ip.expose_magic('aimport', aimport_f)
338 338 ip.set_hook('pre_runcode_hook', runcode_hook)
339 339
340 340 init()
@@ -1,400 +1,400 b''
1 1
2 2 """ Implementations for various useful completers
3 3
4 4 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
5 5 but the basic idea is to do:
6 6
7 7 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
8 8
9 9 """
10 import IPython.ipapi
10 import IPython.core import ipapi
11 11 import glob,os,shlex,sys
12 12 import inspect
13 13 from time import time
14 14 from zipimport import zipimporter
15 ip = IPython.ipapi.get()
15 ip = ipapi.get()
16 16
17 17 try:
18 18 set
19 19 except:
20 20 from sets import Set as set
21 21
22 22 TIMEOUT_STORAGE = 3 #Time in seconds after which the rootmodules will be stored
23 23 TIMEOUT_GIVEUP = 20 #Time in seconds after which we give up
24 24
25 25 def quick_completer(cmd, completions):
26 26 """ Easily create a trivial completer for a command.
27 27
28 28 Takes either a list of completions, or all completions in string
29 29 (that will be split on whitespace)
30 30
31 31 Example::
32 32
33 33 [d:\ipython]|1> import ipy_completers
34 34 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
35 35 [d:\ipython]|3> foo b<TAB>
36 36 bar baz
37 37 [d:\ipython]|3> foo ba
38 38 """
39 39 if isinstance(completions, basestring):
40 40
41 41 completions = completions.split()
42 42 def do_complete(self,event):
43 43 return completions
44 44
45 45 ip.set_hook('complete_command',do_complete, str_key = cmd)
46 46
47 47 def getRootModules():
48 48 """
49 49 Returns a list containing the names of all the modules available in the
50 50 folders of the pythonpath.
51 51 """
52 52 modules = []
53 53 if ip.db.has_key('rootmodules'):
54 54 return ip.db['rootmodules']
55 55 t = time()
56 56 store = False
57 57 for path in sys.path:
58 58 modules += moduleList(path)
59 59 if time() - t >= TIMEOUT_STORAGE and not store:
60 60 store = True
61 61 print "\nCaching the list of root modules, please wait!"
62 62 print "(This will only be done once - type '%rehashx' to " + \
63 63 "reset cache!)"
64 64 print
65 65 if time() - t > TIMEOUT_GIVEUP:
66 66 print "This is taking too long, we give up."
67 67 print
68 68 ip.db['rootmodules'] = []
69 69 return []
70 70
71 71 modules += sys.builtin_module_names
72 72
73 73 modules = list(set(modules))
74 74 if '__init__' in modules:
75 75 modules.remove('__init__')
76 76 modules = list(set(modules))
77 77 if store:
78 78 ip.db['rootmodules'] = modules
79 79 return modules
80 80
81 81 def moduleList(path):
82 82 """
83 83 Return the list containing the names of the modules available in the given
84 84 folder.
85 85 """
86 86
87 87 if os.path.isdir(path):
88 88 folder_list = os.listdir(path)
89 89 elif path.endswith('.egg'):
90 90 try:
91 91 folder_list = [f for f in zipimporter(path)._files]
92 92 except:
93 93 folder_list = []
94 94 else:
95 95 folder_list = []
96 96 #folder_list = glob.glob(os.path.join(path,'*'))
97 97 folder_list = [p for p in folder_list \
98 98 if os.path.exists(os.path.join(path, p,'__init__.py'))\
99 99 or p[-3:] in ('.py','.so')\
100 100 or p[-4:] in ('.pyc','.pyo','.pyd')]
101 101
102 102 folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
103 103 return folder_list
104 104
105 105 def moduleCompletion(line):
106 106 """
107 107 Returns a list containing the completion possibilities for an import line.
108 108 The line looks like this :
109 109 'import xml.d'
110 110 'from xml.dom import'
111 111 """
112 112 def tryImport(mod, only_modules=False):
113 113 def isImportable(module, attr):
114 114 if only_modules:
115 115 return inspect.ismodule(getattr(module, attr))
116 116 else:
117 117 return not(attr[:2] == '__' and attr[-2:] == '__')
118 118 try:
119 119 m = __import__(mod)
120 120 except:
121 121 return []
122 122 mods = mod.split('.')
123 123 for module in mods[1:]:
124 124 m = getattr(m,module)
125 125 if (not hasattr(m, '__file__')) or (not only_modules) or\
126 126 (hasattr(m, '__file__') and '__init__' in m.__file__):
127 127 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
128 128 completion_list.extend(getattr(m,'__all__',[]))
129 129 if hasattr(m, '__file__') and '__init__' in m.__file__:
130 130 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
131 131 completion_list = list(set(completion_list))
132 132 if '__init__' in completion_list:
133 133 completion_list.remove('__init__')
134 134 return completion_list
135 135
136 136 words = line.split(' ')
137 137 if len(words) == 3 and words[0] == 'from':
138 138 return ['import ']
139 139 if len(words) < 3 and (words[0] in ['import','from']) :
140 140 if len(words) == 1:
141 141 return getRootModules()
142 142 mod = words[1].split('.')
143 143 if len(mod) < 2:
144 144 return getRootModules()
145 145 completion_list = tryImport('.'.join(mod[:-1]), True)
146 146 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
147 147 return completion_list
148 148 if len(words) >= 3 and words[0] == 'from':
149 149 mod = words[1]
150 150 return tryImport(mod)
151 151
152 152 def vcs_completer(commands, event):
153 153 """ utility to make writing typical version control app completers easier
154 154
155 155 VCS command line apps typically have the format:
156 156
157 157 [sudo ]PROGNAME [help] [command] file file...
158 158
159 159 """
160 160
161 161
162 162 cmd_param = event.line.split()
163 163 if event.line.endswith(' '):
164 164 cmd_param.append('')
165 165
166 166 if cmd_param[0] == 'sudo':
167 167 cmd_param = cmd_param[1:]
168 168
169 169 if len(cmd_param) == 2 or 'help' in cmd_param:
170 170 return commands.split()
171 171
172 172 return ip.IP.Completer.file_matches(event.symbol)
173 173
174 174
175 175 pkg_cache = None
176 176
177 177 def module_completer(self,event):
178 178 """ Give completions after user has typed 'import ...' or 'from ...'"""
179 179
180 180 # This works in all versions of python. While 2.5 has
181 181 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
182 182 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
183 183 # of possibly problematic side effects.
184 184 # This search the folders in the sys.path for available modules.
185 185
186 186 return moduleCompletion(event.line)
187 187
188 188
189 189 svn_commands = """\
190 190 add blame praise annotate ann cat checkout co cleanup commit ci copy
191 191 cp delete del remove rm diff di export help ? h import info list ls
192 192 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
193 193 pe propget pget pg proplist plist pl propset pset ps resolved revert
194 194 status stat st switch sw unlock update
195 195 """
196 196
197 197 def svn_completer(self,event):
198 198 return vcs_completer(svn_commands, event)
199 199
200 200
201 201 hg_commands = """
202 202 add addremove annotate archive backout branch branches bundle cat
203 203 clone commit copy diff export grep heads help identify import incoming
204 204 init locate log manifest merge outgoing parents paths pull push
205 205 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
206 206 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
207 207 qselect qseries qtop qunapplied recover remove rename revert rollback
208 208 root serve showconfig status strip tag tags tip unbundle update verify
209 209 version
210 210 """
211 211
212 212 def hg_completer(self,event):
213 213 """ Completer for mercurial commands """
214 214
215 215 return vcs_completer(hg_commands, event)
216 216
217 217
218 218
219 219 __bzr_commands = None
220 220
221 221 def bzr_commands():
222 222 global __bzr_commands
223 223 if __bzr_commands is not None:
224 224 return __bzr_commands
225 225 out = os.popen('bzr help commands')
226 226 __bzr_commands = [l.split()[0] for l in out]
227 227 return __bzr_commands
228 228
229 229 def bzr_completer(self,event):
230 230 """ Completer for bazaar commands """
231 231 cmd_param = event.line.split()
232 232 if event.line.endswith(' '):
233 233 cmd_param.append('')
234 234
235 235 if len(cmd_param) > 2:
236 236 cmd = cmd_param[1]
237 237 param = cmd_param[-1]
238 238 output_file = (param == '--output=')
239 239 if cmd == 'help':
240 240 return bzr_commands()
241 241 elif cmd in ['bundle-revisions','conflicts',
242 242 'deleted','nick','register-branch',
243 243 'serve','unbind','upgrade','version',
244 244 'whoami'] and not output_file:
245 245 return []
246 246 else:
247 247 # the rest are probably file names
248 248 return ip.IP.Completer.file_matches(event.symbol)
249 249
250 250 return bzr_commands()
251 251
252 252
253 253 def shlex_split(x):
254 254 """Helper function to split lines into segments."""
255 255 #shlex.split raise exception if syntax error in sh syntax
256 256 #for example if no closing " is found. This function keeps dropping
257 257 #the last character of the line until shlex.split does not raise
258 258 #exception. Adds end of the line to the result of shlex.split
259 259 #example: %run "c:/python -> ['%run','"c:/python']
260 260 endofline=[]
261 261 while x!="":
262 262 try:
263 263 comps=shlex.split(x)
264 264 if len(endofline)>=1:
265 265 comps.append("".join(endofline))
266 266 return comps
267 267 except ValueError:
268 268 endofline=[x[-1:]]+endofline
269 269 x=x[:-1]
270 270 return ["".join(endofline)]
271 271
272 272 def runlistpy(self, event):
273 273 comps = shlex_split(event.line)
274 274 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
275 275
276 276 #print "\nev=",event # dbg
277 277 #print "rp=",relpath # dbg
278 278 #print 'comps=',comps # dbg
279 279
280 280 lglob = glob.glob
281 281 isdir = os.path.isdir
282 282 if relpath.startswith('~'):
283 283 relpath = os.path.expanduser(relpath)
284 284 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
285 285 if isdir(f)]
286 286
287 287 # Find if the user has already typed the first filename, after which we
288 288 # should complete on all files, since after the first one other files may
289 289 # be arguments to the input script.
290 290 #filter(
291 291 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy') or
292 292 f.endswith('.pyw'),comps):
293 293 pys = [f.replace('\\','/') for f in lglob('*')]
294 294 else:
295 295 pys = [f.replace('\\','/')
296 296 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
297 297 lglob(relpath + '*.pyw')]
298 298 return dirs + pys
299 299
300 300
301 301 greedy_cd_completer = False
302 302
303 303 def cd_completer(self, event):
304 304 relpath = event.symbol
305 305 #print event # dbg
306 306 if '-b' in event.line:
307 307 # return only bookmark completions
308 308 bkms = self.db.get('bookmarks',{})
309 309 return bkms.keys()
310 310
311 311
312 312 if event.symbol == '-':
313 313 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
314 314 # jump in directory history by number
315 315 fmt = '-%0' + width_dh +'d [%s]'
316 316 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
317 317 if len(ents) > 1:
318 318 return ents
319 319 return []
320 320
321 321 if event.symbol.startswith('--'):
322 322 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
323 323
324 324 if relpath.startswith('~'):
325 325 relpath = os.path.expanduser(relpath).replace('\\','/')
326 326 found = []
327 327 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
328 328 if os.path.isdir(f)]:
329 329 if ' ' in d:
330 330 # we don't want to deal with any of that, complex code
331 331 # for this is elsewhere
332 raise IPython.ipapi.TryNext
332 raise ipapi.TryNext
333 333 found.append( d )
334 334
335 335 if not found:
336 336 if os.path.isdir(relpath):
337 337 return [relpath]
338 338 # if no completions so far, try bookmarks
339 339 bks = self.db.get('bookmarks',{}).keys()
340 340 bkmatches = [s for s in bks if s.startswith(event.symbol)]
341 341 if bkmatches:
342 342 return bkmatches
343 343
344 raise IPython.ipapi.TryNext
344 raise ipapi.TryNext
345 345
346 346
347 347 def single_dir_expand(matches):
348 348 "Recursively expand match lists containing a single dir."
349 349
350 350 if len(matches) == 1 and os.path.isdir(matches[0]):
351 351 # Takes care of links to directories also. Use '/'
352 352 # explicitly, even under Windows, so that name completions
353 353 # don't end up escaped.
354 354 d = matches[0]
355 355 if d[-1] in ['/','\\']:
356 356 d = d[:-1]
357 357
358 358 subdirs = [p for p in os.listdir(d) if os.path.isdir( d + '/' + p) and not p.startswith('.')]
359 359 if subdirs:
360 360 matches = [ (d + '/' + p) for p in subdirs ]
361 361 return single_dir_expand(matches)
362 362 else:
363 363 return matches
364 364 else:
365 365 return matches
366 366
367 367 if greedy_cd_completer:
368 368 return single_dir_expand(found)
369 369 else:
370 370 return found
371 371
372 372 def apt_get_packages(prefix):
373 373 out = os.popen('apt-cache pkgnames')
374 374 for p in out:
375 375 if p.startswith(prefix):
376 376 yield p.rstrip()
377 377
378 378
379 379 apt_commands = """\
380 380 update upgrade install remove purge source build-dep dist-upgrade
381 381 dselect-upgrade clean autoclean check"""
382 382
383 383 def apt_completer(self, event):
384 384 """ Completer for apt-get (uses apt-cache internally)
385 385
386 386 """
387 387
388 388
389 389 cmd_param = event.line.split()
390 390 if event.line.endswith(' '):
391 391 cmd_param.append('')
392 392
393 393 if cmd_param[0] == 'sudo':
394 394 cmd_param = cmd_param[1:]
395 395
396 396 if len(cmd_param) == 2 or 'help' in cmd_param:
397 397 return apt_commands.split()
398 398
399 399 return list(apt_get_packages(event.symbol))
400 400
@@ -1,62 +1,62 b''
1 1 """ Set default options for IPython.
2 2
3 3 Just import this module to get reasonable defaults for everything.
4 4
5 5 These configurations used to be performed in ipythonrc (or ipythonrc.ini).
6 6 Therefore importing this in your config files makes ipython basically
7 7 ignore your ipythonrc. This is *not* imported by default, you need to import
8 8 this manually in one of your config files.
9 9
10 10 You can further override these defaults in e.g. your ipy_user_config.py,
11 11 ipy_profile_PROFILENAME etc.
12 12
13 13 """
14 14
15 15 import IPython.rlineimpl as readline
16 import IPython.ipapi
17 ip = IPython.ipapi.get()
16 from IPython.core import ipapi
17 ip = ipapi.get()
18 18
19 19 o = ip.options
20 20
21 21 o.colors = "Linux"
22 22 o.color_info=1
23 23 o.confirm_exit=1
24 24 o.pprint=1
25 25 o.multi_line_specials=1
26 26 o.xmode="Context"
27 27
28 28
29 29 o.prompt_in1='In [\#]: '
30 30 o.prompt_in2 =' .\D.: '
31 31 o.prompt_out = 'Out[\#]: '
32 32 o.prompts_pad_left=1
33 33
34 34 o.autoindent = 1
35 35
36 36 o.readline_remove_delims="-/~"
37 37 o.readline_merge_completions=1
38 38
39 39 o.readline = 1
40 40
41 41 rlopts = """\
42 42 tab: complete
43 43 "\C-l": possible-completions
44 44 set show-all-if-ambiguous on
45 45 "\C-o": tab-insert
46 46 "\M-i": " "
47 47 "\M-o": "\d\d\d\d"
48 48 "\M-I": "\d\d\d\d"
49 49 "\C-r": reverse-search-history
50 50 "\C-s": forward-search-history
51 51 "\C-p": history-search-backward
52 52 "\C-n": history-search-forward
53 53 "\e[A": history-search-backward
54 54 "\e[B": history-search-forward
55 55 "\C-k": kill-line
56 56 "\C-u": unix-line-discard"""
57 57
58 58 if readline.have_readline:
59 59 for cmd in rlopts.split('\n'):
60 60 readline.parse_and_bind(cmd)
61 61
62 62
@@ -1,88 +1,88 b''
1 1 """ 'editor' hooks for common editors that work well with ipython
2 2
3 3 They should honor the line number argument, at least.
4 4
5 5 Contributions are *very* welcome.
6 6 """
7 7
8 import IPython.ipapi
9 ip = IPython.ipapi.get()
8 from IPython.core import ipapi
9 ip = ipapi.get()
10 10
11 11 from IPython.Itpl import itplns
12 12 import os
13 13
14 14 def install_editor(run_template, wait = False):
15 15 """ Gets a template in format "myeditor bah bah $file bah bah $line"
16 16
17 17 $file will be replaced by file name, $line by line number (or 0).
18 18 Installs the editor that is called by IPython, instead of the default
19 19 notepad or vi.
20 20
21 21 If wait is true, wait until the user presses enter before returning,
22 22 to facilitate non-blocking editors that exit immediately after
23 23 the call.
24 24 """
25 25
26 26 def call_editor(self, file, line=0):
27 27 if line is None:
28 28 line = 0
29 29 cmd = itplns(run_template, locals())
30 30 print ">",cmd
31 31 if os.system(cmd) != 0:
32 raise IPython.ipapi.TryNext()
32 raise ipapi.TryNext()
33 33 if wait:
34 34 raw_input("Press Enter when done editing:")
35 35
36 36 ip.set_hook('editor',call_editor)
37 37
38 38
39 39 # in these, exe is always the path/name of the executable. Useful
40 40 # if you don't have the editor directory in your path
41 41
42 42 def komodo(exe = 'komodo'):
43 43 """ Activestate Komodo [Edit] """
44 44 install_editor(exe + ' -l $line "$file"', wait = True)
45 45
46 46 def scite(exe = "scite"):
47 47 """ SciTE or Sc1 """
48 48 install_editor(exe + ' "$file" -goto:$line')
49 49
50 50 def notepadplusplus(exe = 'notepad++'):
51 51 """ Notepad++ http://notepad-plus.sourceforge.net """
52 52 install_editor(exe + ' -n$line "$file"')
53 53
54 54 def jed(exe = 'jed'):
55 55 """ JED, the lightweight emacsish editor """
56 56 install_editor(exe + ' +$line "$file"')
57 57
58 58 def idle(exe = None):
59 59 """ Idle, the editor bundled with python
60 60
61 61 Should be pretty smart about finding the executable.
62 62 """
63 63 if exe is None:
64 64 import idlelib
65 65 p = os.path.dirname(idlelib.__file__)
66 66 exe = p + '/idle.py'
67 67 install_editor(exe + ' "$file"')
68 68
69 69 def mate(exe = 'mate'):
70 70 """ TextMate, the missing editor"""
71 71 install_editor(exe + ' -w -l $line "$file"')
72 72
73 73 # these are untested, report any problems
74 74
75 75 def emacs(exe = 'emacs'):
76 76 install_editor(exe + ' +$line "$file"')
77 77
78 78 def gnuclient(exe= 'gnuclient'):
79 79 install_editor(exe + ' -nw +$line "$file"')
80 80
81 81 def crimson_editor(exe = 'cedt.exe'):
82 82 install_editor(exe + ' /L:$line "$file"')
83 83
84 84 def kate(exe = 'kate'):
85 85 install_editor(exe + ' -u -l $line "$file"')
86 86
87 87
88 88 No newline at end of file
@@ -1,76 +1,76 b''
1 import IPython.ipapi
2 ip = IPython.ipapi.get()
1 from IPython.core import ipapi
2 ip = ipapi.get()
3 3
4 4 import os,pprint
5 5
6 6 def export(filename = None):
7 7
8 lines = ['import IPython.ipapi', 'ip = IPython.ipapi.get()','']
8 lines = ['import IPython.core.ipapi', 'ip = IPython.core.ipapi.get()','']
9 9
10 10 vars = ip.db.keys('autorestore/*')
11 11 vars.sort()
12 12 varstomove = []
13 13 get = ip.db.get
14 14
15 15 macros = []
16 16 variables = []
17 17
18 18 for var in vars:
19 19 k = os.path.basename(var)
20 20 v = get(var)
21 21
22 22 if k.startswith('_'):
23 23 continue
24 24 if isinstance(v, IPython.macro.Macro):
25 25 macros.append((k,v))
26 26 if type(v) in [int, str, float]:
27 27 variables.append((k,v))
28 28
29 29
30 30
31 31 if macros:
32 32 lines.extend(['# === Macros ===' ,''])
33 33 for k,v in macros:
34 34 lines.append("ip.defmacro('%s'," % k)
35 35 for line in v.value.splitlines():
36 36 lines.append(' ' + repr(line+'\n'))
37 37 lines.extend([')', ''])
38 38
39 39 if variables:
40 40 lines.extend(['','# === Variables ===',''])
41 41 for k,v in variables:
42 42 varstomove.append(k)
43 43 lines.append('%s = %s' % (k,repr(v)))
44 44
45 45 lines.append('ip.to_user_ns("%s")' % (' '.join(varstomove)))
46 46
47 47 bkms = ip.db.get('bookmarks',{})
48 48
49 49 if bkms:
50 50 lines.extend(['','# === Bookmarks ===',''])
51 51 lines.append("ip.db['bookmarks'] = %s " % pprint.pformat(bkms, indent = 2) )
52 52
53 53 aliases = ip.db.get('stored_aliases', {} )
54 54
55 55 if aliases:
56 56 lines.extend(['','# === Alias definitions ===',''])
57 57 for k,v in aliases.items():
58 58 try:
59 59 lines.append("ip.defalias('%s', %s)" % (k, repr(v[1])))
60 60 except (AttributeError, TypeError):
61 61 pass
62 62
63 63 env = ip.db.get('stored_env')
64 64 if env:
65 65 lines.extend(['','# === Stored env vars ===',''])
66 66 lines.append("ip.db['stored_env'] = %s " % pprint.pformat(env, indent = 2) )
67 67
68 68
69 69
70 70 out = '\n'.join(lines)
71 71
72 72 if filename:
73 73 open(filename,'w').write(out)
74 74 else:
75 75 print out
76 76
@@ -1,43 +1,43 b''
1 1 """ IPython extension management tools.
2 2
3 3 After installation, you'll have the 'extutil' object in your namespace.
4 4 to.
5 5 """
6 6
7 7 # for the purposes of this module, every module that has the name 'ip' globally
8 8 # installed as below is an IPython extension
9 9
10 import IPython.ipapi
11 ip = IPython.ipapi.get()
10 from IPython.core import ipapi
11 ip = ipapi.get()
12 12
13 13 import sys,textwrap,inspect
14 14
15 15 def indent(s, ind= ' '):
16 16 return '\n'.join([ind +l for l in s.splitlines()])
17 17
18 18 class ExtUtil:
19 19 """ IPython extensios (ipy_* etc.) management utilities """
20 20
21 21 def describe(self):
22 22 for n,mod in self._active():
23 23 doc = inspect.getdoc(mod)
24 24 if doc:
25 25 print '== %s ==' % n
26 26 print indent(doc)
27 27
28 28
29 29 def ls(self):
30 30 """ Show list of installed extensions. """
31 31 for n,m in self._active():
32 32 print '%-20s %s' % (n,m.__file__.replace('\\','/'))
33 33 def _active(self):
34 34 act = []
35 35 for mname,m in sys.modules.items():
36 36 o = getattr(m, 'ip', None)
37 if isinstance(o, IPython.ipapi.IPApi):
37 if isinstance(o, ipapi.IPApi):
38 38 act.append((mname,m))
39 39 act.sort()
40 40 return act
41 41
42 42 extutil = ExtUtil()
43 43 ip.to_user_ns('extutil')
@@ -1,245 +1,245 b''
1 1 """ File system operations
2 2
3 3 Contains: Simple variants of normal unix shell commands (icp, imv, irm,
4 4 imkdir, igrep).
5 5
6 6 Some "otherwise handy" utils ('collect' for gathering files to
7 7 ~/_ipython/collect, 'inote' for collecting single note lines to
8 8 ~/_ipython/note.txt)
9 9
10 10 Mostly of use for bare windows installations where cygwin/equivalent is not
11 11 installed and you would otherwise need to deal with dos versions of the
12 12 commands (that e.g. don't understand / as path separator). These can
13 13 do some useful tricks on their own, though (like use 'mglob' patterns).
14 14
15 15 Not to be confused with ipipe commands (ils etc.) that also start with i.
16 16 """
17 17
18 import IPython.ipapi
19 ip = IPython.ipapi.get()
18 from IPython.core import ipapi
19 ip = ipapi.get()
20 20
21 21 import shutil,os,shlex
22 22 from IPython.external import mglob
23 23 from IPython.external.path import path
24 from IPython.ipapi import UsageError
24 from IPython.core.ipapi import UsageError
25 25 import IPython.utils.generics
26 26
27 27 def parse_args(args):
28 28 """ Given arg string 'CMD files... target', return ([files], target) """
29 29
30 30 tup = args.split(None, 1)
31 31 if len(tup) == 1:
32 32 raise UsageError("Expected arguments for " + tup[0])
33 33
34 34 tup2 = shlex.split(tup[1])
35 35
36 36 flist, trg = mglob.expand(tup2[0:-1]), tup2[-1]
37 37 if not flist:
38 38 raise UsageError("No files found:" + str(tup2[0:-1]))
39 39 return flist, trg
40 40
41 41 def icp(ip,arg):
42 42 """ icp files... targetdir
43 43
44 44 Copy all files to target, creating dirs for target if necessary
45 45
46 46 icp srcdir dstdir
47 47
48 48 Copy srcdir to distdir
49 49
50 50 """
51 51 import distutils.dir_util
52 52
53 53 fs, targetdir = parse_args(arg)
54 54 if not os.path.isdir(targetdir) and len(fs) > 1:
55 55 distutils.dir_util.mkpath(targetdir,verbose =1)
56 56 for f in fs:
57 57 if os.path.isdir(f):
58 58 shutil.copytree(f, targetdir)
59 59 else:
60 60 shutil.copy2(f,targetdir)
61 61 return fs
62 62 ip.defalias("icp",icp)
63 63
64 64 def imv(ip,arg):
65 65 """ imv src tgt
66 66
67 67 Move source to target.
68 68 """
69 69
70 70 fs, target = parse_args(arg)
71 71 if len(fs) > 1:
72 72 assert os.path.isdir(target)
73 73 for f in fs:
74 74 shutil.move(f, target)
75 75 return fs
76 76 ip.defalias("imv",imv)
77 77
78 78 def irm(ip,arg):
79 79 """ irm path[s]...
80 80
81 81 Remove file[s] or dir[s] path. Dirs are deleted recursively.
82 82 """
83 83 try:
84 84 paths = mglob.expand(arg.split(None,1)[1])
85 85 except IndexError:
86 86 raise UsageError("%irm paths...")
87 87 import distutils.dir_util
88 88 for p in paths:
89 89 print "rm",p
90 90 if os.path.isdir(p):
91 91 distutils.dir_util.remove_tree(p, verbose = 1)
92 92 else:
93 93 os.remove(p)
94 94
95 95 ip.defalias("irm",irm)
96 96
97 97 def imkdir(ip,arg):
98 98 """ imkdir path
99 99
100 100 Creates dir path, and all dirs on the road
101 101 """
102 102 import distutils.dir_util
103 103 targetdir = arg.split(None,1)[1]
104 104 distutils.dir_util.mkpath(targetdir,verbose =1)
105 105
106 106 ip.defalias("imkdir",imkdir)
107 107
108 108 def igrep(ip,arg):
109 109 """ igrep PAT files...
110 110
111 111 Very dumb file scan, case-insensitive.
112 112
113 113 e.g.
114 114
115 115 igrep "test this" rec:*.py
116 116
117 117 """
118 118 elems = shlex.split(arg)
119 119 dummy, pat, fs = elems[0], elems[1], mglob.expand(elems[2:])
120 120 res = []
121 121 for f in fs:
122 122 found = False
123 123 for l in open(f):
124 124 if pat.lower() in l.lower():
125 125 if not found:
126 126 print "[[",f,"]]"
127 127 found = True
128 128 res.append(f)
129 129 print l.rstrip()
130 130 return res
131 131
132 132 ip.defalias("igrep",igrep)
133 133
134 134 def collect(ip,arg):
135 135 """ collect foo/a.txt rec:bar=*.py
136 136
137 137 Copies foo/a.txt to ~/_ipython/collect/foo/a.txt and *.py from bar,
138 138 likewise
139 139
140 140 Without args, try to open ~/_ipython/collect dir (in win32 at least).
141 141 """
142 142 from IPython.external.path import path
143 143 basedir = path(ip.options.ipythondir + '/collect')
144 144 try:
145 145 fs = mglob.expand(arg.split(None,1)[1])
146 146 except IndexError:
147 147 os.startfile(basedir)
148 148 return
149 149 for f in fs:
150 150 f = path(f)
151 151 trg = basedir / f.splitdrive()[1].lstrip('/\\')
152 152 if f.isdir():
153 153 print "mkdir",trg
154 154 trg.makedirs()
155 155 continue
156 156 dname = trg.dirname()
157 157 if not dname.isdir():
158 158 dname.makedirs()
159 159 print f,"=>",trg
160 160 shutil.copy2(f,trg)
161 161
162 162 ip.defalias("collect",collect)
163 163
164 164 def inote(ip,arg):
165 165 """ inote Hello world
166 166
167 167 Adds timestamp and Hello world to ~/_ipython/notes.txt
168 168
169 169 Without args, opens notes.txt for editing.
170 170 """
171 171 import time
172 172 fname = ip.options.ipythondir + '/notes.txt'
173 173
174 174 try:
175 175 entry = " === " + time.asctime() + ': ===\n' + arg.split(None,1)[1] + '\n'
176 176 f= open(fname, 'a').write(entry)
177 177 except IndexError:
178 178 ip.IP.hooks.editor(fname)
179 179
180 180 ip.defalias("inote",inote)
181 181
182 182 def pathobj_mangle(p):
183 183 return p.replace(' ', '__').replace('.','DOT')
184 184 def pathobj_unmangle(s):
185 185 return s.replace('__',' ').replace('DOT','.')
186 186
187 187
188 188
189 189 class PathObj(path):
190 190 def __init__(self,p):
191 191 self.path = p
192 192 if p != '.':
193 193 self.ents = [pathobj_mangle(ent) for ent in os.listdir(p)]
194 194 else:
195 195 self.ents = None
196 196 def __complete__(self):
197 197 if self.path != '.':
198 198 return self.ents
199 199 self.ents = [pathobj_mangle(ent) for ent in os.listdir('.')]
200 200 return self.ents
201 201 def __getattr__(self,name):
202 202 if name in self.ents:
203 203 if self.path.endswith('/'):
204 204 sep = ''
205 205 else:
206 206 sep = '/'
207 207
208 208 tgt = self.path + sep + pathobj_unmangle(name)
209 209 #print "tgt",tgt
210 210 if os.path.isdir(tgt):
211 211 return PathObj(tgt)
212 212 if os.path.isfile(tgt):
213 213 return path(tgt)
214 214
215 215 raise AttributeError, name # <<< DON'T FORGET THIS LINE !!
216 216 def __str__(self):
217 217 return self.path
218 218
219 219 def __repr__(self):
220 220 return "<PathObj to %s>" % self.path
221 221
222 222 def __call__(self):
223 223 print "cd:",self.path
224 224 os.chdir(self.path)
225 225
226 226 def complete_pathobj(obj, prev_completions):
227 227 if hasattr(obj,'__complete__'):
228 228 res = obj.__complete__()
229 229 if res:
230 230 return res
231 231 # just return normal attributes of 'path' object if the dir is empty
232 raise IPython.ipapi.TryNext
232 raise ipapi.TryNext
233 233
234 234 complete_pathobj = IPython.utils.generics.complete_object.when_type(PathObj)(complete_pathobj)
235 235
236 236 def test_pathobj():
237 237 #p = PathObj('c:/prj')
238 238 #p2 = p.cgi
239 239 #print p,p2
240 240 rootdir = PathObj("/")
241 241 startmenu = PathObj("d:/Documents and Settings/All Users/Start Menu/Programs")
242 242 cwd = PathObj('.')
243 243 ip.to_user_ns("rootdir startmenu cwd")
244 244
245 245 #test_pathobj() No newline at end of file
@@ -1,38 +1,38 b''
1 1 #!/usr/bin/env python
2 2
3 3
4 4 """
5 5 Add %global magic for GNU Global usage.
6 6
7 7 http://www.gnu.org/software/global/
8 8
9 9 """
10 10
11 import IPython.ipapi
12 ip = IPython.ipapi.get()
11 from IPython.core import ipapi
12 ip = ipapi.get()
13 13 import os
14 14
15 15 # alter to your liking
16 16 global_bin = 'd:/opt/global/bin/global'
17 17
18 18 def global_f(self,cmdline):
19 19 simple = 0
20 20 if '-' not in cmdline:
21 21 cmdline = '-rx ' + cmdline
22 22 simple = 1
23 23
24 24 lines = [l.rstrip() for l in os.popen( global_bin + ' ' + cmdline ).readlines()]
25 25
26 26 if simple:
27 27 parts = [l.split(None,3) for l in lines]
28 28 lines = ['%s [%s]\n%s' % (p[2].rjust(70),p[1],p[3].rstrip()) for p in parts]
29 29 print "\n".join(lines)
30 30
31 31 ip.expose_magic('global', global_f)
32 32
33 33 def global_completer(self,event):
34 34 compl = [l.rstrip() for l in os.popen(global_bin + ' -c ' + event.symbol).readlines()]
35 35 return compl
36 36
37 37 ip.set_hook('complete_command', global_completer, str_key = '%global')
38 38
@@ -1,76 +1,76 b''
1 1 """ Greedy completer extension for IPython
2 2
3 3 Normal tab completer refuses to evaluate nonsafe stuff. This will evaluate
4 4 everything, so you need to consider the consequences of pressing tab
5 5 yourself!
6 6
7 7 Note that this extension simplifies readline interaction by setting
8 8 only whitespace as completer delimiter. If this works well, we will
9 9 do the same in default completer.
10 10
11 11 """
12 from IPython import ipapi
12 from IPython.core import ipapi
13 13 from IPython.utils import generics
14 14 from IPython.utils.genutils import dir2
15 15
16 16 def attr_matches(self, text):
17 17 """Compute matches when text contains a dot.
18 18
19 19 MONKEYPATCHED VERSION (ipy_greedycompleter.py)
20 20
21 21 Assuming the text is of the form NAME.NAME....[NAME], and is
22 22 evaluatable in self.namespace or self.global_namespace, it will be
23 23 evaluated and its attributes (as revealed by dir()) are used as
24 24 possible completions. (For class instances, class members are are
25 25 also considered.)
26 26
27 27 WARNING: this can still invoke arbitrary C code, if an object
28 28 with a __getattr__ hook is evaluated.
29 29
30 30 """
31 31 import re
32 32
33 33 force_complete = 1
34 34 # Another option, seems to work great. Catches things like ''.<tab>
35 35 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
36 36
37 37 if m:
38 38 expr, attr = m.group(1, 3)
39 39 else:
40 40 # force match - eval anything that ends with colon
41 41 if not force_complete:
42 42 return []
43 43
44 44 m2 = re.match(r"(.+)\.(\w*)$", self.lbuf)
45 45 if not m2:
46 46 return []
47 47 expr, attr = m2.group(1,2)
48 48
49 49
50 50 try:
51 51 obj = eval(expr, self.namespace)
52 52 except:
53 53 try:
54 54 obj = eval(expr, self.global_namespace)
55 55 except:
56 56 return []
57 57
58 58 words = dir2(obj)
59 59
60 60 try:
61 61 words = generics.complete_object(obj, words)
62 62 except ipapi.TryNext:
63 63 pass
64 64 # Build match list to return
65 65 n = len(attr)
66 66 res = ["%s.%s" % (expr, w) for w in words if w[:n] == attr ]
67 67 return res
68 68
69 69 def main():
70 70 import IPython.rlineimpl as readline
71 71 readline.set_completer_delims(" \n\t")
72 72 # monkeypatch - the code will be folded to normal completer later on
73 73 import IPython.core.completer
74 74 IPython.core.completer.Completer.attr_matches = attr_matches
75 75
76 76 main() No newline at end of file
@@ -1,311 +1,311 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 %jot magic for lightweight persistence.
4 4
5 5 Stores variables in Struct with some notes in PicleShare database
6 6
7 7
8 8 """
9 9
10 10 from datetime import datetime
11 import IPython.ipapi
12 ip = IPython.ipapi.get()
11 from IPython.core import ipapi
12 ip = ipapi.get()
13 13
14 14 import pickleshare
15 15
16 16 import inspect,pickle,os,sys,textwrap
17 17 from IPython.core.fakemodule import FakeModule
18 18 from IPython.ipstruct import Struct
19 19
20 20
21 21 def refresh_variables(ip, key=None):
22 22 db = ip.db
23 23 if key is None:
24 24 keys = db.keys('jot/*')
25 25 else:
26 26 keys = db.keys('jot/'+key)
27 27 for key in keys:
28 28 # strip autorestore
29 29 justkey = os.path.basename(key)
30 30 print "Restoring from", justkey, "..."
31 31 try:
32 32 obj = db[key]
33 33 except KeyError:
34 34 print "Unable to restore variable '%s', ignoring (use %%jot -d to forget!)" % justkey
35 35 print "The error was:",sys.exc_info()[0]
36 36 else:
37 37 #print "restored",justkey,"=",obj #dbg
38 38 try:
39 39 origname = obj.name
40 40 except:
41 41 ip.user_ns[justkey] = obj
42 42 print "Restored", justkey
43 43 else:
44 44 ip.user_ns[origname] = obj['val']
45 45 print "Restored", origname
46 46
47 47 def read_variables(ip, key=None):
48 48 db = ip.db
49 49 if key is None:
50 50 return None
51 51 else:
52 52 keys = db.keys('jot/'+key)
53 53 for key in keys:
54 54 # strip autorestore
55 55 justkey = os.path.basename(key)
56 56 print "restoring from ", justkey
57 57 try:
58 58 obj = db[key]
59 59 except KeyError:
60 60 print "Unable to read variable '%s', ignoring (use %%jot -d to forget!)" % justkey
61 61 print "The error was:",sys.exc_info()[0]
62 62 else:
63 63 return obj
64 64
65 65
66 66 def detail_variables(ip, key=None):
67 67 db, get = ip.db, ip.db.get
68 68
69 69 if key is None:
70 70 keys = db.keys('jot/*')
71 71 else:
72 72 keys = db.keys('jot/'+key)
73 73 if keys:
74 74 size = max(map(len,keys))
75 75 else:
76 76 size = 0
77 77
78 78 fmthead = '%-'+str(size)+'s [%s]'
79 79 fmtbody = 'Comment:\n %s'
80 80 fmtdata = 'Data:\n %s, %s'
81 81 for key in keys:
82 82 v = get(key,'<unavailable>')
83 83 justkey = os.path.basename(key)
84 84 try:
85 85 print fmthead % (justkey, datetime.ctime(v.get('time','<unavailable>')))
86 86 print fmtbody % (v.get('comment','<unavailable>'))
87 87 d = v.get('val','unavailable')
88 88 print fmtdata % (repr(type(d)), '')
89 89 print repr(d)[0:200]
90 90 print
91 91 print
92 92 except AttributeError:
93 93 print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50])
94 94
95 95
96 96 def intm(n):
97 97 try:
98 98 return int(n)
99 99 except:
100 100 return 0
101 101
102 102 def jot_obj(self, obj, name, comment=''):
103 103 """
104 104 write obj data to the note database, with whatever that should be noted.
105 105 """
106 106 had = self.db.keys('jot/'+name+'*')
107 107 # if it the same name but a later version, we stupidly add a number to the
108 108 # so the name doesn't collide. Any better idea?
109 109 suffix = ''
110 110 if len(had)>0:
111 111 pre = os.path.commonprefix(had)
112 112 suf = [n.split(pre)[1] for n in had]
113 113 versions = map(intm, suf)
114 114 suffix = str(max(versions)+1)
115 115
116 116 uname = 'jot/'+name+suffix
117 117
118 118 # which one works better?
119 119 #all = ip.IP.shadowhist.all()
120 120 all = ip.IP.shell.input_hist
121 121
122 122 # We may actually want to make snapshot of files that are run-ned.
123 123
124 124 # get the comment
125 125 try:
126 126 comment = ip.IP.magic_edit('-x').strip()
127 127 except:
128 128 print "No comment is recorded."
129 129 comment = ''
130 130
131 131 self.db[uname] = Struct({'val':obj,
132 132 'time' : datetime.now(),
133 133 'hist' : all,
134 134 'name' : name,
135 135 'comment' : comment,})
136 136
137 137 print "Jotted down notes for '%s' (%s)" % (uname, obj.__class__.__name__)
138 138
139 139
140 140
141 141 def magic_jot(self, parameter_s=''):
142 142 """Lightweight persistence for python variables.
143 143
144 144 Example:
145 145
146 146 ville@badger[~]|1> A = ['hello',10,'world']\\
147 147 ville@badger[~]|2> %jot A\\
148 148 ville@badger[~]|3> Exit
149 149
150 150 (IPython session is closed and started again...)
151 151
152 152 ville@badger:~$ ipython -p pysh\\
153 153 ville@badger[~]|1> print A
154 154
155 155 ['hello', 10, 'world']
156 156
157 157 Usage:
158 158
159 159 %jot - Show list of all variables and their current values\\
160 160 %jot -l - Show list of all variables and their current values in detail\\
161 161 %jot -l <var> - Show one variable and its current values in detail\\
162 162 %jot <var> - Store the *current* value of the variable to disk\\
163 163 %jot -d <var> - Remove the variable and its value from storage\\
164 164 %jot -z - Remove all variables from storage (disabled)\\
165 165 %jot -r <var> - Refresh/Load variable from jot (delete current vals)\\
166 166 %jot foo >a.txt - Store value of foo to new file a.txt\\
167 167 %jot foo >>a.txt - Append value of foo to file a.txt\\
168 168
169 169 It should be noted that if you change the value of a variable, you
170 170 need to %note it again if you want to persist the new value.
171 171
172 172 Note also that the variables will need to be pickleable; most basic
173 173 python types can be safely %stored.
174 174
175 175 """
176 176
177 177 opts,argsl = self.parse_options(parameter_s,'drzl',mode='string')
178 178 args = argsl.split(None,1)
179 179 ip = self.getapi()
180 180 db = ip.db
181 181 # delete
182 182 if opts.has_key('d'):
183 183 try:
184 184 todel = args[0]
185 185 except IndexError:
186 186 error('You must provide the variable to forget')
187 187 else:
188 188 try:
189 189 del db['jot/' + todel]
190 190 except:
191 191 error("Can't delete variable '%s'" % todel)
192 192 # reset the whole database
193 193 elif opts.has_key('z'):
194 194 print "reseting the whole database has been disabled."
195 195 #for k in db.keys('autorestore/*'):
196 196 # del db[k]
197 197
198 198 elif opts.has_key('r'):
199 199 try:
200 200 toret = args[0]
201 201 except:
202 202 print "restoring all the variables jotted down..."
203 203 refresh_variables(ip)
204 204 else:
205 205 refresh_variables(ip, toret)
206 206
207 207 elif opts.has_key('l'):
208 208 try:
209 209 tolist = args[0]
210 210 except:
211 211 print "List details for all the items."
212 212 detail_variables(ip)
213 213 else:
214 214 print "Details for", tolist, ":"
215 215 detail_variables(ip, tolist)
216 216
217 217 # run without arguments -> list noted variables & notes
218 218 elif not args:
219 219 vars = self.db.keys('jot/*')
220 220 vars.sort()
221 221 if vars:
222 222 size = max(map(len,vars)) - 4
223 223 else:
224 224 size = 0
225 225
226 226 print 'Variables and their in-db values:'
227 227 fmt = '%-'+str(size)+'s [%s] -> %s'
228 228 get = db.get
229 229 for var in vars:
230 230 justkey = os.path.basename(var)
231 231 v = get(var,'<unavailable>')
232 232 try:
233 233 print fmt % (justkey,\
234 234 datetime.ctime(v.get('time','<unavailable>')),\
235 235 v.get('comment','<unavailable>')[:70].replace('\n',' '),)
236 236 except AttributeError:
237 237 print fmt % (justkey, '<unavailable>', '<unavailable>', repr(v)[:50])
238 238
239 239
240 240 # default action - store the variable
241 241 else:
242 242 # %store foo >file.txt or >>file.txt
243 243 if len(args) > 1 and args[1].startswith('>'):
244 244 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
245 245 if args[1].startswith('>>'):
246 246 fil = open(fnam,'a')
247 247 else:
248 248 fil = open(fnam,'w')
249 249 obj = ip.ev(args[0])
250 250 print "Writing '%s' (%s) to file '%s'." % (args[0],
251 251 obj.__class__.__name__, fnam)
252 252
253 253
254 254 if not isinstance (obj,basestring):
255 255 from pprint import pprint
256 256 pprint(obj,fil)
257 257 else:
258 258 fil.write(obj)
259 259 if not obj.endswith('\n'):
260 260 fil.write('\n')
261 261
262 262 fil.close()
263 263 return
264 264
265 265 # %note foo
266 266 try:
267 267 obj = ip.user_ns[args[0]]
268 268 except KeyError:
269 269 # this should not be alias, for aliases, use %store
270 270 print
271 271 print "Error: %s doesn't exist." % args[0]
272 272 print
273 273 print "Use %note -r <var> to retrieve variables. This should not be used " +\
274 274 "to store alias, for saving aliases, use %store"
275 275 return
276 276 else:
277 277 if isinstance(inspect.getmodule(obj), FakeModule):
278 278 print textwrap.dedent("""\
279 279 Warning:%s is %s
280 280 Proper storage of interactively declared classes (or instances
281 281 of those classes) is not possible! Only instances
282 282 of classes in real modules on file system can be %%store'd.
283 283 """ % (args[0], obj) )
284 284 return
285 285 #pickled = pickle.dumps(obj)
286 286 #self.db[ 'jot/' + args[0] ] = obj
287 287 jot_obj(self, obj, args[0])
288 288
289 289
290 290 def magic_read(self, parameter_s=''):
291 291 """
292 292 %read <var> - Load variable from data that is jotted down.\\
293 293
294 294 """
295 295
296 296 opts,argsl = self.parse_options(parameter_s,'drzl',mode='string')
297 297 args = argsl.split(None,1)
298 298 ip = self.getapi()
299 299 db = ip.db
300 300 #if opts.has_key('r'):
301 301 try:
302 302 toret = args[0]
303 303 except:
304 304 print "which record do you want to read out?"
305 305 return
306 306 else:
307 307 return read_variables(ip, toret)
308 308
309 309
310 310 ip.expose_magic('jot',magic_jot)
311 311 ip.expose_magic('read',magic_read)
@@ -1,62 +1,62 b''
1 1 """ Legacy stuff
2 2
3 3 Various stuff that are there for historical / familiarity reasons.
4 4
5 5 This is automatically imported by default profile, though not other profiles
6 6 (e.g. 'sh' profile).
7 7
8 8 Stuff that is considered obsolete / redundant is gradually moved here.
9 9
10 10 """
11 11
12 import IPython.ipapi
13 ip = IPython.ipapi.get()
12 from IPython.core import ipapi
13 ip = ipapi.get()
14 14
15 15 import os,sys
16 16
17 17 from IPython.utils.genutils import *
18 18
19 19 # use rehashx
20 20
21 21 def magic_rehash(self, parameter_s = ''):
22 22 """Update the alias table with all entries in $PATH.
23 23
24 24 This version does no checks on execute permissions or whether the
25 25 contents of $PATH are truly files (instead of directories or something
26 26 else). For such a safer (but slower) version, use %rehashx."""
27 27
28 28 # This function (and rehashx) manipulate the alias_table directly
29 29 # rather than calling magic_alias, for speed reasons. A rehash on a
30 30 # typical Linux box involves several thousand entries, so efficiency
31 31 # here is a top concern.
32 32
33 33 path = filter(os.path.isdir,os.environ.get('PATH','').split(os.pathsep))
34 34 alias_table = self.shell.alias_table
35 35 for pdir in path:
36 36 for ff in os.listdir(pdir):
37 37 # each entry in the alias table must be (N,name), where
38 38 # N is the number of positional arguments of the alias.
39 39 alias_table[ff] = (0,ff)
40 40 # Make sure the alias table doesn't contain keywords or builtins
41 41 self.shell.alias_table_validate()
42 42 # Call again init_auto_alias() so we get 'rm -i' and other modified
43 43 # aliases since %rehash will probably clobber them
44 44 self.shell.init_auto_alias()
45 45
46 46 ip.expose_magic("rehash", magic_rehash)
47 47
48 48 # Exit
49 49 def magic_Quit(self, parameter_s=''):
50 50 """Exit IPython without confirmation (like %Exit)."""
51 51
52 52 self.shell.ask_exit()
53 53
54 54 ip.expose_magic("Quit", magic_Quit)
55 55
56 56
57 57 # make it autocallable fn if you really need it
58 58 def magic_p(self, parameter_s=''):
59 59 """Just a short alias for Python's 'print'."""
60 60 exec 'print ' + parameter_s in self.shell.user_ns
61 61
62 62 ip.expose_magic("p", magic_p)
@@ -1,234 +1,234 b''
1 1 """
2 2 IPython extension: %lookfor command for searching docstrings
3 3
4 4 """
5 5 # Pauli Virtanen <pav@iki.fi>, 2008.
6 6
7 7 import re, inspect, pkgutil, pydoc
8 8
9 9 #------------------------------------------------------------------------------
10 10 # Lookfor functionality
11 11 #------------------------------------------------------------------------------
12 12
13 13 # Cache for lookfor: {id(module): {name: (docstring, kind, index), ...}...}
14 14 # where kind: "func", "class", "module", "object"
15 15 # and index: index in breadth-first namespace traversal
16 16 _lookfor_caches = {}
17 17
18 18 # regexp whose match indicates that the string may contain a function signature
19 19 _function_signature_re = re.compile(r"[a-z_]+\(.*[,=].*\)", re.I)
20 20
21 21 def lookfor(what, modules=None, import_modules=True, regenerate=False):
22 22 """
23 23 Search for objects whose documentation contains all given words.
24 24 Shows a summary of matching objects, sorted roughly by relevance.
25 25
26 26 Parameters
27 27 ----------
28 28 what : str
29 29 String containing words to look for.
30 30
31 31 module : str, module
32 32 Module whose docstrings to go through.
33 33 import_modules : bool
34 34 Whether to import sub-modules in packages.
35 35 Will import only modules in __all__
36 36 regenerate: bool
37 37 Re-generate the docstring cache
38 38
39 39 """
40 40 # Cache
41 41 cache = {}
42 42 for module in modules:
43 43 try:
44 44 c = _lookfor_generate_cache(module, import_modules, regenerate)
45 45 cache.update(c)
46 46 except ImportError:
47 47 pass
48 48
49 49 # Search
50 50 # XXX: maybe using a real stemming search engine would be better?
51 51 found = []
52 52 whats = str(what).lower().split()
53 53 if not whats: return
54 54
55 55 for name, (docstring, kind, index) in cache.iteritems():
56 56 if kind in ('module', 'object'):
57 57 # don't show modules or objects
58 58 continue
59 59 ok = True
60 60 doc = docstring.lower()
61 61 for w in whats:
62 62 if w not in doc:
63 63 ok = False
64 64 break
65 65 if ok:
66 66 found.append(name)
67 67
68 68 # Relevance sort
69 69 # XXX: this is full Harrison-Stetson heuristics now,
70 70 # XXX: it probably could be improved
71 71
72 72 kind_relevance = {'func': 1000, 'class': 1000,
73 73 'module': -1000, 'object': -1000}
74 74
75 75 def relevance(name, docstr, kind, index):
76 76 r = 0
77 77 # do the keywords occur within the start of the docstring?
78 78 first_doc = "\n".join(docstr.lower().strip().split("\n")[:3])
79 79 r += sum([200 for w in whats if w in first_doc])
80 80 # do the keywords occur in the function name?
81 81 r += sum([30 for w in whats if w in name])
82 82 # is the full name long?
83 83 r += -len(name) * 5
84 84 # is the object of bad type?
85 85 r += kind_relevance.get(kind, -1000)
86 86 # is the object deep in namespace hierarchy?
87 87 r += -name.count('.') * 10
88 88 r += max(-index / 100, -100)
89 89 return r
90 90
91 91 def relevance_sort(a, b):
92 92 dr = relevance(b, *cache[b]) - relevance(a, *cache[a])
93 93 if dr != 0: return dr
94 94 else: return cmp(a, b)
95 95 found.sort(relevance_sort)
96 96
97 97 # Pretty-print
98 98 s = "Search results for '%s'" % (' '.join(whats))
99 99 help_text = [s, "-"*len(s)]
100 100 for name in found:
101 101 doc, kind, ix = cache[name]
102 102
103 103 doclines = [line.strip() for line in doc.strip().split("\n")
104 104 if line.strip()]
105 105
106 106 # find a suitable short description
107 107 try:
108 108 first_doc = doclines[0].strip()
109 109 if _function_signature_re.search(first_doc):
110 110 first_doc = doclines[1].strip()
111 111 except IndexError:
112 112 first_doc = ""
113 113 help_text.append("%s\n %s" % (name, first_doc))
114 114
115 115 # Output
116 116 if len(help_text) > 10:
117 117 pager = pydoc.getpager()
118 118 pager("\n".join(help_text))
119 119 else:
120 120 print "\n".join(help_text)
121 121
122 122 def _lookfor_generate_cache(module, import_modules, regenerate):
123 123 """
124 124 Generate docstring cache for given module.
125 125
126 126 Parameters
127 127 ----------
128 128 module : str, None, module
129 129 Module for which to generate docstring cache
130 130 import_modules : bool
131 131 Whether to import sub-modules in packages.
132 132 Will import only modules in __all__
133 133 regenerate: bool
134 134 Re-generate the docstring cache
135 135
136 136 Returns
137 137 -------
138 138 cache : dict {obj_full_name: (docstring, kind, index), ...}
139 139 Docstring cache for the module, either cached one (regenerate=False)
140 140 or newly generated.
141 141
142 142 """
143 143 global _lookfor_caches
144 144
145 145 if module is None:
146 146 module = "numpy"
147 147
148 148 if isinstance(module, str):
149 149 module = __import__(module)
150 150
151 151 if id(module) in _lookfor_caches and not regenerate:
152 152 return _lookfor_caches[id(module)]
153 153
154 154 # walk items and collect docstrings
155 155 cache = {}
156 156 _lookfor_caches[id(module)] = cache
157 157 seen = {}
158 158 index = 0
159 159 stack = [(module.__name__, module)]
160 160 while stack:
161 161 name, item = stack.pop(0)
162 162 if id(item) in seen: continue
163 163 seen[id(item)] = True
164 164
165 165 index += 1
166 166 kind = "object"
167 167
168 168 if inspect.ismodule(item):
169 169 kind = "module"
170 170 try:
171 171 _all = item.__all__
172 172 except AttributeError:
173 173 _all = None
174 174 # import sub-packages
175 175 if import_modules and hasattr(item, '__path__'):
176 176 for m in pkgutil.iter_modules(item.__path__):
177 177 if _all is not None and m[1] not in _all:
178 178 continue
179 179 try:
180 180 __import__("%s.%s" % (name, m[1]))
181 181 except ImportError:
182 182 continue
183 183 for n, v in inspect.getmembers(item):
184 184 if _all is not None and n not in _all:
185 185 continue
186 186 stack.append(("%s.%s" % (name, n), v))
187 187 elif inspect.isclass(item):
188 188 kind = "class"
189 189 for n, v in inspect.getmembers(item):
190 190 stack.append(("%s.%s" % (name, n), v))
191 191 elif callable(item):
192 192 kind = "func"
193 193
194 194 doc = inspect.getdoc(item)
195 195 if doc is not None:
196 196 cache[name] = (doc, kind, index)
197 197
198 198 return cache
199 199
200 200 #------------------------------------------------------------------------------
201 201 # IPython connectivity
202 202 #------------------------------------------------------------------------------
203 203
204 import IPython.ipapi
205 ip = IPython.ipapi.get()
204 from IPython.core import ipapi
205 ip = ipapi.get()
206 206
207 207 _lookfor_modules = ['numpy', 'scipy']
208 208
209 209 def lookfor_f(self, arg=''):
210 210 r"""
211 211 Search for objects whose documentation contains all given words.
212 212 Shows a summary of matching objects, sorted roughly by relevance.
213 213
214 214 Usage
215 215 -----
216 216 %lookfor +numpy some words
217 217 Search module 'numpy'
218 218
219 219 %lookfor_modules numpy scipy
220 220 Set default modules whose docstrings to search
221 221
222 222 """
223 223 lookfor(arg, modules=_lookfor_modules)
224 224
225 225 def lookfor_modules_f(self, arg=''):
226 226 global _lookfor_modules
227 227 if not arg:
228 228 print "Modules included in %lookfor search:", _lookfor_modules
229 229 else:
230 230 _lookfor_modules = arg.split()
231 231
232 232 ip.expose_magic('lookfor', lookfor_f)
233 233 ip.expose_magic('lookfor_modules', lookfor_modules_f)
234 234
@@ -1,47 +1,47 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Add %p4 magic for pythonic p4 (Perforce) usage.
4 4 """
5 5
6 import IPython.ipapi
7 ip = IPython.ipapi.get()
6 from IPython.core import ipapi
7 ip = ipapi.get()
8 8
9 9 import os,sys,marshal
10 10
11 11 import ipy_stock_completers
12 12
13 13 def p4_f(self, parameter_s=''):
14 14 cmd = 'p4 -G ' + parameter_s
15 15 fobj = os.popen(cmd)
16 16 out = []
17 17 while 1:
18 18 try:
19 19 out.append(marshal.load(fobj))
20 20 except EOFError:
21 21 break
22 22
23 23 return out
24 24
25 25 def p4d(fname):
26 26 return os.popen('p4 where ' + fname).read().split()[0]
27 27
28 28 ip.to_user_ns("p4d")
29 29
30 30 ip.expose_magic('p4', p4_f)
31 31
32 32 p4_commands = """\
33 33 add admin annotate branch branches change changes changelist
34 34 changelists client clients counter counters delete depot depots
35 35 describe diff diff2 dirs edit filelog files fix fixes flush fstat
36 36 group groups have help info integrate integrated job jobs jobspec
37 37 label labels labelsync lock logger login logout monitor obliterate
38 38 opened passwd print protect rename reopen resolve resolved revert
39 39 review reviews set submit sync tag tickets triggers typemap unlock
40 40 user users verify workspace workspaces where"""
41 41
42 42 def p4_completer(self,event):
43 43 return ipy_stock_completers.vcs_completer(p4_commands, event)
44 44
45 45 ip.set_hook('complete_command', p4_completer, str_key = '%p4')
46 46 ip.set_hook('complete_command', p4_completer, str_key = 'p4')
47 47
@@ -1,132 +1,132 b''
1 1 """ Use pretty.py for configurable pretty-printing.
2 2
3 3 Register pretty-printers for types using ipy_pretty.for_type() or
4 4 ipy_pretty.for_type_by_name(). For example, to use the example pretty-printer
5 5 for numpy dtype objects, add the following to your ipy_user_conf.py::
6 6
7 7 from IPython.Extensions import ipy_pretty
8 8
9 9 ipy_pretty.activate()
10 10
11 11 # If you want to have numpy always imported anyways:
12 12 import numpy
13 13 ipy_pretty.for_type(numpy.dtype, ipy_pretty.dtype_pprinter)
14 14
15 15 # If you don't want to have numpy imported until it needs to be:
16 16 ipy_pretty.for_type_by_name('numpy', 'dtype', ipy_pretty.dtype_pprinter)
17 17 """
18 18
19 import IPython.ipapi
19 from IPython.core import ipapi
20 20 from IPython.utils.genutils import Term
21 21
22 22 from IPython.external import pretty
23 23
24 ip = IPython.ipapi.get()
24 ip = ipapi.get()
25 25
26 26
27 27 #### Implementation ############################################################
28 28
29 29 def pretty_result_display(self, arg):
30 30 """ Uber-pretty-printing display hook.
31 31
32 32 Called for displaying the result to the user.
33 33 """
34 34
35 35 if ip.options.pprint:
36 36 verbose = getattr(ip.options, 'pretty_verbose', False)
37 37 out = pretty.pretty(arg, verbose=verbose)
38 38 if '\n' in out:
39 39 # So that multi-line strings line up with the left column of
40 40 # the screen, instead of having the output prompt mess up
41 41 # their first line.
42 42 Term.cout.write('\n')
43 43 print >>Term.cout, out
44 44 else:
45 45 raise TryNext
46 46
47 47
48 48 #### API #######################################################################
49 49
50 50 # Expose the for_type and for_type_by_name functions for easier use.
51 51 for_type = pretty.for_type
52 52 for_type_by_name = pretty.for_type_by_name
53 53
54 54
55 55 # FIXME: write deactivate(). We need a way to remove a hook.
56 56 def activate():
57 57 """ Activate this extension.
58 58 """
59 59 ip.set_hook('result_display', pretty_result_display, priority=99)
60 60
61 61
62 62 #### Example pretty-printers ###################################################
63 63
64 64 def dtype_pprinter(obj, p, cycle):
65 65 """ A pretty-printer for numpy dtype objects.
66 66 """
67 67 if cycle:
68 68 return p.text('dtype(...)')
69 69 if obj.fields is None:
70 70 p.text(repr(obj))
71 71 else:
72 72 p.begin_group(7, 'dtype([')
73 73 for i, field in enumerate(obj.descr):
74 74 if i > 0:
75 75 p.text(',')
76 76 p.breakable()
77 77 p.pretty(field)
78 78 p.end_group(7, '])')
79 79
80 80
81 81 #### Tests #####################################################################
82 82
83 83 def test_pretty():
84 84 """
85 85 In [1]: from IPython.Extensions import ipy_pretty
86 86
87 87 In [2]: ipy_pretty.activate()
88 88
89 89 In [3]: class A(object):
90 90 ...: def __repr__(self):
91 91 ...: return 'A()'
92 92 ...:
93 93 ...:
94 94
95 95 In [4]: a = A()
96 96
97 97 In [5]: a
98 98 Out[5]: A()
99 99
100 100 In [6]: def a_pretty_printer(obj, p, cycle):
101 101 ...: p.text('<A>')
102 102 ...:
103 103 ...:
104 104
105 105 In [7]: ipy_pretty.for_type(A, a_pretty_printer)
106 106
107 107 In [8]: a
108 108 Out[8]: <A>
109 109
110 110 In [9]: class B(object):
111 111 ...: def __repr__(self):
112 112 ...: return 'B()'
113 113 ...:
114 114 ...:
115 115
116 116 In [10]: B.__module__, B.__name__
117 117 Out[10]: ('__main__', 'B')
118 118
119 119 In [11]: def b_pretty_printer(obj, p, cycle):
120 120 ....: p.text('<B>')
121 121 ....:
122 122 ....:
123 123
124 124 In [12]: ipy_pretty.for_type_by_name('__main__', 'B', b_pretty_printer)
125 125
126 126 In [13]: b = B()
127 127
128 128 In [14]: b
129 129 Out[14]: <B>
130 130 """
131 131 assert False, "This should only be doctested, not run."
132 132
@@ -1,46 +1,46 b''
1 1 """Config file for 'doctest' profile.
2 2
3 3 This profile modifies the prompts to be the standard Python ones, so that you
4 4 can generate easily doctests from an IPython session.
5 5
6 6 But more importantly, it enables pasting of code with '>>>' prompts and
7 7 arbitrary initial whitespace, as is typical of doctests in reST files and
8 8 docstrings. This allows you to easily re-run existing doctests and iteratively
9 9 work on them as part of your development workflow.
10 10
11 11 The exception mode is also set to 'plain' so the generated exceptions are as
12 12 similar as possible to the default Python ones, for inclusion in doctests."""
13 13
14 14 # get various stuff that are there for historical / familiarity reasons
15 15 import ipy_legacy
16 16
17 from IPython import ipapi
17 from IPython.core import ipapi
18 18
19 19 from IPython.Extensions import InterpreterPasteInput
20 20
21 21 def main():
22 22 ip = ipapi.get()
23 23 o = ip.options
24 24
25 25 # Set the prompts similar to the defaults
26 26 o.prompt_in1 = '>>> '
27 27 o.prompt_in2 = '... '
28 28 o.prompt_out = ''
29 29
30 30 # Add a blank line before each new set of inputs. This is needed by
31 31 # doctest to distinguish each test from the next.
32 32 o.separate_in = '\n'
33 33 o.separate_out = ''
34 34 o.separate_out2 = ''
35 35
36 36 # Disable pprint, so that outputs are printed as similarly to standard
37 37 # python as possible
38 38 o.pprint = False
39 39
40 40 # Use plain exceptions, to also resemble normal pyhton.
41 41 o.xmode = 'plain'
42 42
43 43 # Store the activity flag in the metadata bag from the running shell
44 44 ip.IP.meta.doctest_mode = True
45 45
46 46 main()
@@ -1,24 +1,24 b''
1 1 """ IPython 'numpy' profile, to preload NumPy.
2 2
3 3 This profile loads the math/cmath modules as well as all of numpy.
4 4
5 5 It exposes numpy via the 'np' shorthand as well for convenience.
6 6 """
7 7
8 import IPython.ipapi
8 from IPython.core import ipapi
9 9 import ipy_defaults
10 10
11 11 def main():
12 ip = IPython.ipapi.get()
12 ip = ipapi.get()
13 13
14 14 try:
15 15 ip.ex("import math,cmath")
16 16 ip.ex("import numpy")
17 17 ip.ex("import numpy as np")
18 18
19 19 ip.ex("from numpy import *")
20 20
21 21 except ImportError:
22 22 print "Unable to start NumPy profile, is numpy installed?"
23 23
24 24 main()
@@ -1,29 +1,29 b''
1 1 """ IPython 'scipy' profile, preloads NumPy and SciPy.
2 2
3 3 This profile loads the math/cmath modules as well as all of numpy and scipy.
4 4
5 5 It exposes numpy and scipy via the 'np' and 'sp' shorthands as well for
6 6 convenience.
7 7 """
8 8
9 import IPython.ipapi
9 from IPython.core import ipapi
10 10 import ipy_defaults
11 11
12 12 def main():
13 ip = IPython.ipapi.get()
13 ip = ipapi.get()
14 14
15 15 try:
16 16 ip.ex("import math,cmath")
17 17 ip.ex("import numpy")
18 18 ip.ex("import scipy")
19 19
20 20 ip.ex("import numpy as np")
21 21 ip.ex("import scipy as sp")
22 22
23 23 ip.ex("from numpy import *")
24 24 ip.ex("from scipy import *")
25 25
26 26 except ImportError:
27 27 print "Unable to start scipy profile, are numpy and scipy installed?"
28 28
29 29 main()
@@ -1,270 +1,270 b''
1 1 """Shell mode for IPython.
2 2
3 3 Start ipython in shell mode by invoking "ipython -p sh"
4 4
5 5 (the old version, "ipython -p pysh" still works but this is the more "modern"
6 6 shell mode and is recommended for users who don't care about pysh-mode
7 7 compatibility)
8 8 """
9 9
10 from IPython import ipapi
10 from IPython.core import ipapi
11 11 import os,re,textwrap
12 12
13 13 # The import below effectively obsoletes your old-style ipythonrc[.ini],
14 14 # so consider yourself warned!
15 15
16 16 import ipy_defaults
17 17
18 18 def main():
19 19 ip = ipapi.get()
20 20 o = ip.options
21 21 # autocall to "full" mode (smart mode is default, I like full mode)
22 22
23 23 o.autocall = 2
24 24
25 25 # Jason Orendorff's path class is handy to have in user namespace
26 26 # if you are doing shell-like stuff
27 27 try:
28 28 ip.ex("from IPython.external.path import path" )
29 29 except ImportError:
30 30 pass
31 31
32 32 # beefed up %env is handy in shell mode
33 33 import envpersist
34 34
35 35 # To see where mycmd resides (in path/aliases), do %which mycmd
36 36 import ipy_which
37 37
38 38 # tab completers for hg, svn, ...
39 39 import ipy_app_completers
40 40
41 41 # To make executables foo and bar in mybin usable without PATH change, do:
42 42 # %rehashdir c:/mybin
43 43 # %store foo
44 44 # %store bar
45 45 import ipy_rehashdir
46 46
47 47 # does not work without subprocess module!
48 48 #import ipy_signals
49 49
50 50 ip.ex('import os')
51 51 ip.ex("def up(): os.chdir('..')")
52 52 ip.user_ns['LA'] = LastArgFinder()
53 53
54 54 # You can assign to _prompt_title variable
55 55 # to provide some extra information for prompt
56 56 # (e.g. the current mode, host/username...)
57 57
58 58 ip.user_ns['_prompt_title'] = ''
59 59
60 60 # Nice prompt
61 61 o.prompt_in1= r'\C_Green${_prompt_title}\C_LightBlue[\C_LightCyan\Y2\C_LightBlue]\C_Green|\#> '
62 62 o.prompt_in2= r'\C_Green|\C_LightGreen\D\C_Green> '
63 63 o.prompt_out= '<\#> '
64 64
65 65 from IPython import Release
66 66
67 67 import sys
68 68 # Non-chatty banner
69 69 o.banner = "IPython %s [on Py %s]\n" % (Release.version,sys.version.split(None,1)[0])
70 70
71 71
72 72 ip.IP.default_option('cd','-q')
73 73 ip.IP.default_option('macro', '-r')
74 74 # If you only rarely want to execute the things you %edit...
75 75 #ip.IP.default_option('edit','-x')
76 76
77 77
78 78 o.prompts_pad_left="1"
79 79 # Remove all blank lines in between prompts, like a normal shell.
80 80 o.separate_in="0"
81 81 o.separate_out="0"
82 82 o.separate_out2="0"
83 83
84 84 # now alias all syscommands
85 85
86 86 db = ip.db
87 87
88 88 syscmds = db.get("syscmdlist",[] )
89 89 if not syscmds:
90 90 print textwrap.dedent("""
91 91 System command list not initialized, probably the first run...
92 92 running %rehashx to refresh the command list. Run %rehashx
93 93 again to refresh command list (after installing new software etc.)
94 94 """)
95 95 ip.magic('rehashx')
96 96 syscmds = db.get("syscmdlist")
97 97
98 98 # lowcase aliases on win32 only
99 99 if os.name == 'posix':
100 100 mapper = lambda s:s
101 101 else:
102 102 def mapper(s): return s.lower()
103 103
104 104 for cmd in syscmds:
105 105 # print "sys",cmd #dbg
106 106 noext, ext = os.path.splitext(cmd)
107 107 if ext.lower() == '.exe':
108 108 cmd = noext
109 109
110 110 key = mapper(cmd)
111 111 if key not in ip.IP.alias_table:
112 112 # Dots will be removed from alias names, since ipython
113 113 # assumes names with dots to be python code
114 114
115 115 ip.defalias(key.replace('.',''), cmd)
116 116
117 117 # mglob combines 'find', recursion, exclusion... '%mglob?' to learn more
118 118 ip.load("IPython.external.mglob")
119 119
120 120 # win32 is crippled w/o cygwin, try to help it a little bit
121 121 if sys.platform == 'win32':
122 122 if 'cygwin' in os.environ['PATH'].lower():
123 123 # use the colors of cygwin ls (recommended)
124 124 ip.defalias('d', 'ls -F --color=auto')
125 125 else:
126 126 # get icp, imv, imkdir, igrep, irm,...
127 127 ip.load('ipy_fsops')
128 128
129 129 # and the next best thing to real 'ls -F'
130 130 ip.defalias('d','dir /w /og /on')
131 131
132 132 ip.set_hook('input_prefilter', slash_prefilter_f)
133 133 extend_shell_behavior(ip)
134 134
135 135 class LastArgFinder:
136 136 """ Allow $LA to work as "last argument of previous command", like $! in bash
137 137
138 138 To call this in normal IPython code, do LA()
139 139 """
140 140 def __call__(self, hist_idx = None):
141 141 ip = ipapi.get()
142 142 if hist_idx is None:
143 143 return str(self)
144 144 return ip.IP.input_hist_raw[hist_idx].strip().split()[-1]
145 145 def __str__(self):
146 146 ip = ipapi.get()
147 147 for cmd in reversed(ip.IP.input_hist_raw):
148 148 parts = cmd.strip().split()
149 149 if len(parts) < 2 or parts[-1] in ['$LA', 'LA()']:
150 150 continue
151 151 return parts[-1]
152 152 return ""
153 153
154 154 def slash_prefilter_f(self,line):
155 155 """ ./foo, ~/foo and /bin/foo now run foo as system command
156 156
157 157 Removes the need for doing !./foo, !~/foo or !/bin/foo
158 158 """
159 159 from IPython.utils import genutils
160 160 if re.match('(?:[.~]|/[a-zA-Z_0-9]+)/', line):
161 161 return "_ip.system(" + genutils.make_quoted_expr(line)+")"
162 162 raise ipapi.TryNext
163 163
164 164 # XXX You do not need to understand the next function!
165 165 # This should probably be moved out of profile
166 166
167 167 def extend_shell_behavior(ip):
168 168
169 169 # Instead of making signature a global variable tie it to IPSHELL.
170 170 # In future if it is required to distinguish between different
171 171 # shells we can assign a signature per shell basis
172 172 ip.IP.__sig__ = 0xa005
173 173 # mark the IPSHELL with this signature
174 174 ip.IP.user_ns['__builtins__'].__dict__['__sig__'] = ip.IP.__sig__
175 175
176 176 from IPython.Itpl import ItplNS
177 177 from IPython.genutils import shell
178 178 # utility to expand user variables via Itpl
179 179 # xxx do something sensible with depth?
180 180 ip.IP.var_expand = lambda cmd, lvars=None, depth=2: \
181 181 str(ItplNS(cmd, ip.IP.user_ns, get_locals()))
182 182
183 183 def get_locals():
184 184 """ Substituting a variable through Itpl deep inside the IPSHELL stack
185 185 requires the knowledge of all the variables in scope upto the last
186 186 IPSHELL frame. This routine simply merges all the local variables
187 187 on the IPSHELL stack without worrying about their scope rules
188 188 """
189 189 import sys
190 190 # note lambda expression constitues a function call
191 191 # hence fno should be incremented by one
192 192 getsig = lambda fno: sys._getframe(fno+1).f_globals \
193 193 ['__builtins__'].__dict__['__sig__']
194 194 getlvars = lambda fno: sys._getframe(fno+1).f_locals
195 195 # trackback until we enter the IPSHELL
196 196 frame_no = 1
197 197 sig = ip.IP.__sig__
198 198 fsig = ~sig
199 199 while fsig != sig :
200 200 try:
201 201 fsig = getsig(frame_no)
202 202 except (AttributeError, KeyError):
203 203 frame_no += 1
204 204 except ValueError:
205 205 # stack is depleted
206 206 # call did not originate from IPSHELL
207 207 return {}
208 208 first_frame = frame_no
209 209 # walk further back until we exit from IPSHELL or deplete stack
210 210 try:
211 211 while(sig == getsig(frame_no+1)):
212 212 frame_no += 1
213 213 except (AttributeError, KeyError, ValueError):
214 214 pass
215 215 # merge the locals from top down hence overriding
216 216 # any re-definitions of variables, functions etc.
217 217 lvars = {}
218 218 for fno in range(frame_no, first_frame-1, -1):
219 219 lvars.update(getlvars(fno))
220 220 #print '\n'*5, first_frame, frame_no, '\n', lvars, '\n'*5 #dbg
221 221 return lvars
222 222
223 223 def _runlines(lines):
224 224 """Run a string of one or more lines of source.
225 225
226 226 This method is capable of running a string containing multiple source
227 227 lines, as if they had been entered at the IPython prompt. Since it
228 228 exposes IPython's processing machinery, the given strings can contain
229 229 magic calls (%magic), special shell access (!cmd), etc."""
230 230
231 231 # We must start with a clean buffer, in case this is run from an
232 232 # interactive IPython session (via a magic, for example).
233 233 ip.IP.resetbuffer()
234 234 lines = lines.split('\n')
235 235 more = 0
236 236 command = ''
237 237 for line in lines:
238 238 # skip blank lines so we don't mess up the prompt counter, but do
239 239 # NOT skip even a blank line if we are in a code block (more is
240 240 # true)
241 241 # if command is not empty trim the line
242 242 if command != '' :
243 243 line = line.strip()
244 244 # add the broken line to the command
245 245 if line and line[-1] == '\\' :
246 246 command += line[0:-1] + ' '
247 247 more = True
248 248 continue
249 249 else :
250 250 # add the last (current) line to the command
251 251 command += line
252 252 if command or more:
253 253 # push to raw history, so hist line numbers stay in sync
254 254 ip.IP.input_hist_raw.append("# " + command + "\n")
255 255
256 256 more = ip.IP.push(ip.IP.prefilter(command,more))
257 257 command = ''
258 258 # IPython's runsource returns None if there was an error
259 259 # compiling the code. This allows us to stop processing right
260 260 # away, so the user gets the error message at the right place.
261 261 if more is None:
262 262 break
263 263 # final newline in case the input didn't have it, so that the code
264 264 # actually does get executed
265 265 if more:
266 266 ip.IP.push('\n')
267 267
268 268 ip.IP.runlines = _runlines
269 269
270 270 main()
@@ -1,321 +1,321 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ An ipython profile for zope and plone.
3 3
4 4 Some ideas stolen from http://www.tomster.org.
5 5
6 6
7 7 Authors
8 8 -------
9 9 - Stefan Eletzhofer <stefan.eletzhofer@inquant.de>
10 10 """
11 11
12 12 # File: ipy_profile_zope.py
13 13 #
14 14 # Copyright (c) InQuant GmbH
15 15 #
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19
20 from IPython import ipapi
20 from IPython.core import ipapi
21 21 from IPython import Release
22 22 from types import StringType
23 23 import sys
24 24 import os
25 25 import textwrap
26 26
27 27 # The import below effectively obsoletes your old-style ipythonrc[.ini],
28 28 # so consider yourself warned!
29 29 # import ipy_defaults
30 30
31 31 _marker = []
32 32 def shasattr(obj, attr, acquire=False):
33 33 """ See Archetypes/utils.py
34 34 """
35 35 if not acquire:
36 36 obj = obj.aq_base
37 37 return getattr(obj, attr, _marker) is not _marker
38 38
39 39 class ZopeDebug(object):
40 40 def __init__(self):
41 41
42 42 self.instancehome = os.environ.get( "INSTANCE_HOME" )
43 43
44 44 configfile = os.environ.get( "CONFIG_FILE" )
45 45 if configfile is None and self.instancehome is not None:
46 46 configfile = os.path.join( self.instancehome, "etc", "zope.conf" )
47 47
48 48 if configfile is None:
49 49 raise RuntimeError( "CONFIG_FILE env not set" )
50 50
51 51 print "CONFIG_FILE=", configfile
52 52 print "INSTANCE_HOME=", self.instancehome
53 53
54 54 self.configfile = configfile
55 55
56 56 try:
57 57 from Zope2 import configure
58 58 except ImportError:
59 59 from Zope import configure
60 60
61 61 configure( configfile )
62 62
63 63 try:
64 64 import Zope2
65 65 app = Zope2.app()
66 66 except ImportError:
67 67 import Zope
68 68 app = Zope.app()
69 69
70 70 from Testing.makerequest import makerequest
71 71 self.app = makerequest( app )
72 72
73 73 try:
74 74 self._make_permissive()
75 75 print "Permissive security installed"
76 76 except:
77 77 print "Permissive security NOT installed"
78 78
79 79 self._pwd = self.portal or self.app
80 80
81 81 try:
82 82 from zope.component import getSiteManager
83 83 from zope.component import getGlobalSiteManager
84 84 from zope.app.component.hooks import setSite
85 85
86 86 if self.portal is not None:
87 87 setSite( self.portal )
88 88
89 89 gsm = getGlobalSiteManager()
90 90 sm = getSiteManager()
91 91
92 92 if sm is gsm:
93 93 print "ERROR SETTING SITE!"
94 94 except:
95 95 pass
96 96
97 97
98 98 @property
99 99 def utils(self):
100 100 class Utils(object):
101 101 commit = self.commit
102 102 sync = self.sync
103 103 objectInfo = self.objectInfo
104 104 ls = self.ls
105 105 pwd = self.pwd
106 106 cd = self.cd
107 107 su = self.su
108 108 getCatalogInfo = self.getCatalogInfo
109 109
110 110 @property
111 111 def cwd(self):
112 112 return self.pwd()
113 113
114 114 return Utils()
115 115
116 116 @property
117 117 def namespace(self):
118 118 return dict( utils=self.utils, app=self.app, portal=self.portal )
119 119
120 120 @property
121 121 def portal(self):
122 122 portals = self.app.objectValues( "Plone Site" )
123 123 if len(portals):
124 124 return portals[0]
125 125 else:
126 126 raise KeyError( "No Plone Site found.")
127 127
128 128 def pwd(self):
129 129 return self._pwd
130 130
131 131 def _make_permissive(self):
132 132 """
133 133 Make a permissive security manager with all rights. Hell,
134 134 we're developers, aren't we? Security is for whimps. :)
135 135 """
136 136 from Products.CMFCore.tests.base.security import PermissiveSecurityPolicy
137 137 import AccessControl
138 138 from AccessControl.SecurityManagement import newSecurityManager
139 139 from AccessControl.SecurityManager import setSecurityPolicy
140 140
141 141 _policy = PermissiveSecurityPolicy()
142 142 self.oldpolicy = setSecurityPolicy(_policy)
143 143 newSecurityManager(None, AccessControl.User.system)
144 144
145 145 def su(self, username):
146 146 """ Change to named user.
147 147 """
148 148 # TODO Make it easy to change back to permissive security.
149 149 user = self.portal.acl_users.getUser(username)
150 150 if not user:
151 151 print "Can't find %s in %s" % (username, self.portal.acl_users)
152 152 return
153 153
154 154 from AccessControl import ZopeSecurityPolicy
155 155 import AccessControl
156 156 from AccessControl.SecurityManagement import newSecurityManager, getSecurityManager
157 157 from AccessControl.SecurityManager import setSecurityPolicy
158 158
159 159 _policy = ZopeSecurityPolicy
160 160 self.oldpolicy = setSecurityPolicy(_policy)
161 161 wrapped_user = user.__of__(self.portal.acl_users)
162 162 newSecurityManager(None, user)
163 163 print 'User changed.'
164 164 return getSecurityManager().getUser()
165 165
166 166 def getCatalogInfo(self, obj=None, catalog='portal_catalog', query=None, sort_on='created', sort_order='reverse' ):
167 167 """ Inspect portal_catalog. Pass an object or object id for a
168 168 default query on that object, or pass an explicit query.
169 169 """
170 170 if obj and query:
171 171 print "Ignoring %s, using query." % obj
172 172
173 173 catalog = self.portal.get(catalog)
174 174 if not catalog:
175 175 return 'No catalog'
176 176
177 177 indexes = catalog._catalog.indexes
178 178 if not query:
179 179 if type(obj) is StringType:
180 180 cwd = self.pwd()
181 181 obj = cwd.unrestrictedTraverse( obj )
182 182 # If the default in the signature is mutable, its value will
183 183 # persist across invocations.
184 184 query = {}
185 185 if indexes.get('path'):
186 186 from string import join
187 187 path = join(obj.getPhysicalPath(), '/')
188 188 query.update({'path': path})
189 189 if indexes.get('getID'):
190 190 query.update({'getID': obj.id, })
191 191 if indexes.get('UID') and shasattr(obj, 'UID'):
192 192 query.update({'UID': obj.UID(), })
193 193 if indexes.get(sort_on):
194 194 query.update({'sort_on': sort_on, 'sort_order': sort_order})
195 195 if not query:
196 196 return 'Empty query'
197 197 results = catalog(**query)
198 198
199 199 result_info = []
200 200 for r in results:
201 201 rid = r.getRID()
202 202 if rid:
203 203 result_info.append(
204 204 {'path': catalog.getpath(rid),
205 205 'metadata': catalog.getMetadataForRID(rid),
206 206 'indexes': catalog.getIndexDataForRID(rid), }
207 207 )
208 208 else:
209 209 result_info.append({'missing': rid})
210 210
211 211 if len(result_info) == 1:
212 212 return result_info[0]
213 213 return result_info
214 214
215 215 def commit(self):
216 216 """
217 217 Commit the transaction.
218 218 """
219 219 try:
220 220 import transaction
221 221 transaction.get().commit()
222 222 except ImportError:
223 223 get_transaction().commit()
224 224
225 225 def sync(self):
226 226 """
227 227 Sync the app's view of the zodb.
228 228 """
229 229 self.app._p_jar.sync()
230 230
231 231 def objectInfo( self, o ):
232 232 """
233 233 Return a descriptive string of an object
234 234 """
235 235 Title = ""
236 236 t = getattr( o, 'Title', None )
237 237 if t:
238 238 Title = t()
239 239 return {'id': o.getId(),
240 240 'Title': Title,
241 241 'portal_type': getattr( o, 'portal_type', o.meta_type),
242 242 'folderish': o.isPrincipiaFolderish
243 243 }
244 244
245 245 def cd( self, path ):
246 246 """
247 247 Change current dir to a specific folder.
248 248
249 249 cd( ".." )
250 250 cd( "/plone/Members/admin" )
251 251 cd( portal.Members.admin )
252 252 etc.
253 253 """
254 254 if type(path) is not StringType:
255 255 path = '/'.join(path.getPhysicalPath())
256 256 cwd = self.pwd()
257 257 x = cwd.unrestrictedTraverse( path )
258 258 if x is None:
259 259 raise KeyError( "Can't cd to %s" % path )
260 260
261 261 print "%s -> %s" % ( self.pwd().getId(), x.getId() )
262 262 self._pwd = x
263 263
264 264 def ls( self, x=None ):
265 265 """
266 266 List object(s)
267 267 """
268 268 if type(x) is StringType:
269 269 cwd = self.pwd()
270 270 x = cwd.unrestrictedTraverse( x )
271 271 if x is None:
272 272 x = self.pwd()
273 273 if x.isPrincipiaFolderish:
274 274 return [self.objectInfo(o) for id, o in x.objectItems()]
275 275 else:
276 276 return self.objectInfo( x )
277 277
278 278 zope_debug = None
279 279
280 280 def ipy_set_trace():
281 281 from IPython.core import debugger
282 282 debugger.Pdb().set_trace()
283 283
284 284 def main():
285 285 global zope_debug
286 286 ip = ipapi.get()
287 287 o = ip.options
288 288 # autocall to "full" mode (smart mode is default, I like full mode)
289 289
290 290 SOFTWARE_HOME = os.environ.get( "SOFTWARE_HOME" )
291 291 sys.path.append( SOFTWARE_HOME )
292 292 print "SOFTWARE_HOME=%s\n" % SOFTWARE_HOME
293 293
294 294 zope_debug = ZopeDebug()
295 295
296 296 # <HACK ALERT>
297 297 import pdb;
298 298 pdb.set_trace = ipy_set_trace
299 299 # </HACK ALERT>
300 300
301 301 # I like my banner minimal.
302 302 o.banner = "ZOPE Py %s IPy %s\n" % (sys.version.split('\n')[0],Release.version)
303 303
304 304 print textwrap.dedent("""\
305 305 ZOPE mode iPython shell.
306 306
307 307 Bound names:
308 308 app
309 309 portal
310 310 utils.{ %s }
311 311
312 312 Uses the $SOFTWARE_HOME and $CONFIG_FILE environment
313 313 variables.
314 314 """ % ( ",".join([ x for x in dir(zope_debug.utils) if not x.startswith("_") ] ) ) )
315 315
316 316
317 317 ip.user_ns.update( zope_debug.namespace )
318 318
319 319
320 320 main()
321 321 # vim: set ft=python ts=4 sw=4 expandtab :
@@ -1,31 +1,31 b''
1 1 import inspect
2 import IPython.ipapi
2 from IPython.core import ipapi
3 3 from IPython.utils.genutils import arg_split
4 ip = IPython.ipapi.get()
4 ip = ipapi.get()
5 5
6 6 from IPython.core import debugger
7 7
8 8 def call_pydb(self, args):
9 9 """Invoke pydb with the supplied parameters."""
10 10 try:
11 11 import pydb
12 12 except ImportError:
13 13 raise ImportError("pydb doesn't seem to be installed.")
14 14
15 15 if not hasattr(pydb.pydb, "runv"):
16 16 raise ImportError("You need pydb version 1.19 or later installed.")
17 17
18 18 argl = arg_split(args)
19 19 # print argl # dbg
20 20 if len(inspect.getargspec(pydb.runv)[0]) == 2:
21 21 pdb = debugger.Pdb(color_scheme=self.rc.colors)
22 22 ip.IP.history_saving_wrapper( lambda : pydb.runv(argl, pdb) )()
23 23 else:
24 24 ip.IP.history_saving_wrapper( lambda : pydb.runv(argl) )()
25 25
26 26
27 27 ip.expose_magic("pydb",call_pydb)
28 28
29 29
30 30
31 31
@@ -1,140 +1,140 b''
1 1 # -*- coding: utf-8 -*-
2 2 """ IPython extension: add %rehashdir magic
3 3
4 4 Usage:
5 5
6 6 %rehashdir c:/bin c:/tools
7 7 - Add all executables under c:/bin and c:/tools to alias table, in
8 8 order to make them directly executable from any directory.
9 9
10 10 This also serves as an example on how to extend ipython
11 11 with new magic functions.
12 12
13 13 Unlike rest of ipython, this requires Python 2.4 (optional
14 14 extensions are allowed to do that).
15 15
16 16 """
17 17
18 import IPython.ipapi
19 ip = IPython.ipapi.get()
18 from IPython.core import ipapi
19 ip = ipapi.get()
20 20
21 21
22 22 import os,re,fnmatch,sys
23 23
24 24 def selflaunch(ip,line):
25 25 """ Launch python script with 'this' interpreter
26 26
27 27 e.g. d:\foo\ipykit.exe a.py
28 28
29 29 """
30 30
31 31 tup = line.split(None,1)
32 32 if len(tup) == 1:
33 33 print "Launching nested ipython session"
34 34 os.system(sys.executable)
35 35 return
36 36
37 37 cmd = sys.executable + ' ' + tup[1]
38 38 print ">",cmd
39 39 os.system(cmd)
40 40
41 41 class PyLauncher:
42 42 """ Invoke selflanucher on the specified script
43 43
44 44 This is mostly useful for associating with scripts using::
45 45 _ip.defalias('foo',PyLauncher('foo_script.py'))
46 46
47 47 """
48 48 def __init__(self,script):
49 49 self.script = os.path.abspath(script)
50 50 def __call__(self, ip, line):
51 51 if self.script.endswith('.ipy'):
52 52 ip.runlines(open(self.script).read())
53 53 else:
54 54 # first word is the script/alias name itself, strip it
55 55 tup = line.split(None,1)
56 56 if len(tup) == 2:
57 57 tail = ' ' + tup[1]
58 58 else:
59 59 tail = ''
60 60
61 61 selflaunch(ip,"py " + self.script + tail)
62 62 def __repr__(self):
63 63 return 'PyLauncher("%s")' % self.script
64 64
65 65 def rehashdir_f(self,arg):
66 66 """ Add executables in all specified dirs to alias table
67 67
68 68 Usage:
69 69
70 70 %rehashdir c:/bin;c:/tools
71 71 - Add all executables under c:/bin and c:/tools to alias table, in
72 72 order to make them directly executable from any directory.
73 73
74 74 Without arguments, add all executables in current directory.
75 75
76 76 """
77 77
78 78 # most of the code copied from Magic.magic_rehashx
79 79
80 80 def isjunk(fname):
81 81 junk = ['*~']
82 82 for j in junk:
83 83 if fnmatch.fnmatch(fname, j):
84 84 return True
85 85 return False
86 86
87 87 created = []
88 88 if not arg:
89 89 arg = '.'
90 90 path = map(os.path.abspath,arg.split(';'))
91 91 alias_table = self.shell.alias_table
92 92
93 93 if os.name == 'posix':
94 94 isexec = lambda fname:os.path.isfile(fname) and \
95 95 os.access(fname,os.X_OK)
96 96 else:
97 97
98 98 try:
99 99 winext = os.environ['pathext'].replace(';','|').replace('.','')
100 100 except KeyError:
101 101 winext = 'exe|com|bat|py'
102 102 if 'py' not in winext:
103 103 winext += '|py'
104 104
105 105 execre = re.compile(r'(.*)\.(%s)$' % winext,re.IGNORECASE)
106 106 isexec = lambda fname:os.path.isfile(fname) and execre.match(fname)
107 107 savedir = os.getcwd()
108 108 try:
109 109 # write the whole loop for posix/Windows so we don't have an if in
110 110 # the innermost part
111 111 if os.name == 'posix':
112 112 for pdir in path:
113 113 os.chdir(pdir)
114 114 for ff in os.listdir(pdir):
115 115 if isexec(ff) and not isjunk(ff):
116 116 # each entry in the alias table must be (N,name),
117 117 # where N is the number of positional arguments of the
118 118 # alias.
119 119 src,tgt = os.path.splitext(ff)[0], os.path.abspath(ff)
120 120 created.append(src)
121 121 alias_table[src] = (0,tgt)
122 122 else:
123 123 for pdir in path:
124 124 os.chdir(pdir)
125 125 for ff in os.listdir(pdir):
126 126 if isexec(ff) and not isjunk(ff):
127 127 src, tgt = execre.sub(r'\1',ff), os.path.abspath(ff)
128 128 src = src.lower()
129 129 created.append(src)
130 130 alias_table[src] = (0,tgt)
131 131 # Make sure the alias table doesn't contain keywords or builtins
132 132 self.shell.alias_table_validate()
133 133 # Call again init_auto_alias() so we get 'rm -i' and other
134 134 # modified aliases since %rehashx will probably clobber them
135 135 # self.shell.init_auto_alias()
136 136 finally:
137 137 os.chdir(savedir)
138 138 return created
139 139
140 140 ip.expose_magic("rehashdir",rehashdir_f)
@@ -1,68 +1,68 b''
1 1 #!/usr/bin/env python
2 2
3 3 """ IPython extension: Render templates from variables and paste to clipbard """
4 4
5 import IPython.ipapi
5 from IPython.core import ipapi
6 6
7 ip = IPython.ipapi.get()
7 ip = ipapi.get()
8 8
9 9 from string import Template
10 10 import sys,os
11 11
12 12 from IPython.Itpl import itplns
13 13
14 14 def toclip_w32(s):
15 15 """ Places contents of s to clipboard
16 16
17 17 Needs pyvin32 to work:
18 18 http://sourceforge.net/projects/pywin32/
19 19 """
20 20 import win32clipboard as cl
21 21 import win32con
22 22 cl.OpenClipboard()
23 23 cl.EmptyClipboard()
24 24 cl.SetClipboardText( s.replace('\n','\r\n' ))
25 25 cl.CloseClipboard()
26 26
27 27 try:
28 28 import win32clipboard
29 29 toclip = toclip_w32
30 30 except ImportError:
31 31 def toclip(s): pass
32 32
33 33
34 34 def render(tmpl):
35 35 """ Render a template (Itpl format) from ipython variables
36 36
37 37 Example:
38 38
39 39 $ import ipy_render
40 40 $ my_name = 'Bob' # %store this for convenience
41 41 $ t_submission_form = "Submission report, author: $my_name" # %store also
42 42 $ render t_submission_form
43 43
44 44 => returns "Submission report, author: Bob" and copies to clipboard on win32
45 45
46 46 # if template exist as a file, read it. Note: ;f hei vaan => f("hei vaan")
47 47 $ ;render c:/templates/greeting.txt
48 48
49 49 Template examples (Ka-Ping Yee's Itpl library):
50 50
51 51 Here is a $string.
52 52 Here is a $module.member.
53 53 Here is an $object.member.
54 54 Here is a $functioncall(with, arguments).
55 55 Here is an ${arbitrary + expression}.
56 56 Here is an $array[3] member.
57 57 Here is a $dictionary['member'].
58 58 """
59 59
60 60 if os.path.isfile(tmpl):
61 61 tmpl = open(tmpl).read()
62 62
63 63 res = itplns(tmpl, ip.user_ns)
64 64 toclip(res)
65 65 return res
66 66
67 67 ip.to_user_ns('render')
68 68 No newline at end of file
@@ -1,38 +1,38 b''
1 1 """ Simple TCP socket server that executes statements in IPython instance.
2 2
3 3 Usage:
4 4
5 5 import ipy_server
6 6 ipy_server.serve_thread(16455)
7 7
8 8 Now, to execute the statements in this ipython instance, open a TCP socket
9 9 (port 16455), write out the statements, and close the socket.
10 10 You can use e.g. "telnet localhost 16455" or a script to do this.
11 11
12 12 This is a bit like 'M-x server-start" or gnuserv in the emacs world.
13 13
14 14 """
15 15
16 import IPython.ipapi
17 ip = IPython.ipapi.get()
16 from IPython.core import ipapi
17 ip = ipapi.get()
18 18
19 19 import SocketServer
20 20
21 21 # user-accessible port
22 22 PORT = 8099
23 23
24 24 class IPythonRequestHandler(SocketServer.StreamRequestHandler):
25 25 def handle(self):
26 26 #print "connection from", self.client_address
27 27 inp = self.rfile.read().replace('\r\n','\n')
28 28 #print "Execute",inp
29 29 ip.runlines(inp)
30 30
31 31 def serve(port = PORT):
32 32 server = SocketServer.TCPServer(("", port), IPythonRequestHandler)
33 33 print "ipy_server on TCP port", port
34 34 server.serve_forever()
35 35
36 36 def serve_thread(port = PORT):
37 37 import thread
38 38 thread.start_new_thread(serve, (port,)) No newline at end of file
@@ -1,62 +1,62 b''
1 1 """ Advanced signal (e.g. ctrl+C) handling for IPython
2 2
3 3 So far, this only ignores ctrl + C in IPython file a subprocess
4 4 is executing, to get closer to how a "proper" shell behaves.
5 5
6 6 Other signal processing may be implemented later on.
7 7
8 8 If _ip.options.verbose is true, show exit status if nonzero
9 9
10 10 """
11 11
12 12 import signal,os,sys
13 import IPython.ipapi
13 from IPython.core import ipapi
14 14 import subprocess
15 15
16 ip = IPython.ipapi.get()
16 ip = ipapi.get()
17 17
18 18 def new_ipsystem_posix(cmd):
19 19 """ ctrl+c ignoring replacement for system() command in iplib.
20 20
21 21 Ignore ctrl + c in IPython process during the command execution.
22 22 The subprocess will still get the ctrl + c signal.
23 23
24 24 posix implementation
25 25 """
26 26
27 27 p = subprocess.Popen(cmd, shell = True)
28 28
29 29 old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
30 30 pid,status = os.waitpid(p.pid,0)
31 31 signal.signal(signal.SIGINT, old_handler)
32 32 if status and ip.options.verbose:
33 33 print "[exit status: %d]" % status
34 34
35 35 def new_ipsystem_win32(cmd):
36 36 """ ctrl+c ignoring replacement for system() command in iplib.
37 37
38 38 Ignore ctrl + c in IPython process during the command execution.
39 39 The subprocess will still get the ctrl + c signal.
40 40
41 41 win32 implementation
42 42 """
43 43 old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
44 44 status = os.system(cmd)
45 45 signal.signal(signal.SIGINT, old_handler)
46 46 if status and ip.options.verbose:
47 47 print "[exit status: %d]" % status
48 48
49 49
50 50 def init():
51 51 o = ip.options
52 52 try:
53 53 o.verbose
54 54 except AttributeError:
55 55 o.allow_new_attr (True )
56 56 o.verbose = 0
57 57
58 58 ip.IP.system = (sys.platform == 'win32' and new_ipsystem_win32 or
59 59 new_ipsystem_posix)
60 60
61 61 init()
62 62 No newline at end of file
@@ -1,17 +1,17 b''
1 1 """ Install various IPython completers
2 2
3 3 IPython extension that installs completers related to core ipython behaviour.
4 4
5 5 The actual implementations are in Extensions/ipy_completers.py
6 6
7 7 """
8 import IPython.ipapi
8 from IPython.core import ipapi
9 9
10 ip = IPython.ipapi.get()
10 ip = ipapi.get()
11 11
12 12 from ipy_completers import *
13 13
14 14 ip.set_hook('complete_command', module_completer, str_key = 'import')
15 15 ip.set_hook('complete_command', module_completer, str_key = 'from')
16 16 ip.set_hook('complete_command', runlistpy, str_key = '%run')
17 17 ip.set_hook('complete_command', cd_completer, str_key = '%cd')
@@ -1,242 +1,242 b''
1 import IPython.ipapi
2 ip = IPython.ipapi.get()
1 from IPython.core import ipapi
2 ip = ipapi.get()
3 3
4 4 import win32api
5 5 import win32ui
6 6 import win32console
7 7 import dde
8 8 import os
9 9 import scitedirector
10 10
11 11 # test to write.
12 12
13 13 def set_hook(synchronize_with_editor):
14 14 """Set the synchronize with editor hook with a callable object.
15 15
16 16 The callable object will be called with the following arguments when
17 17 IPython wants to synchronize with you favorite editor:
18 18
19 19 - ip: a running IPython instance.
20 20
21 21 - filename: the path of the file the editor is supposed to display.
22 22
23 23 - lineno : the line number of the line the editor is supposed to
24 24 highlight.
25 25
26 26 - columnno : the column number of the character the editor is supposed
27 27 to highlight.
28 28 """
29 29 ip.set_hook("synchronize_with_editor", synchronize_with_editor)
30 30
31 31
32 32 def find_filename(filename):
33 33 """Return the filename to synchronize with based on """
34 34 filename = os.path.splitext(filename)
35 35 if filename[1] == ".pyc":
36 36 filename = (filename[0], ".py")
37 37 filename = "".join(filename)
38 38
39 39 if not os.path.isabs(filename):
40 40 filename = os.path.join(os.getcwd(), filename)
41 41
42 42 if os.path.isfile(filename):
43 43 return filename
44 44
45 45 return ""
46 46
47 47
48 48 def run_command(path, command, arguments, asynchronous = True):
49 49 """Run a shell command and return the exit code of the command"""
50 50 # This is a thin wrapper around os.system that:
51 51 # - Let you run command asynchronously.
52 52 # - Accept spaces in command path.
53 53 # - Dont throw exception if the command don't exist.
54 54 line = ''
55 55 if asynchronous:
56 56 line += 'start '
57 57
58 58 try:
59 59 line += win32api.GetShortPathName(os.path.join(path, command) + ".exe") + " "
60 60 except:
61 61 print 'could not find: "%s"' % (os.path.join(path, command) + ".exe")
62 62 return -1
63 63
64 64 line += arguments
65 65 r = os.system(line)
66 66 return r
67 67
68 68
69 69 def sleep(milliseconds):
70 70 """Wait some milliseconds."""
71 71 # This is used to make sure the editor did its job before we reset the focus on the console.
72 72 win32api.Sleep(milliseconds)
73 73
74 74
75 75 def restore_console_focus():
76 76 """Restore the focus to the IPython console."""
77 77 h = win32console.GetConsoleWindow()
78 78 console_window = win32ui.CreateWindowFromHandle(h)
79 79 console_window.SetForegroundWindow()
80 80
81 81
82 82 # This is the most simple example of hook:
83 83 class GVimHook:
84 84 def __init__(self, path, wakeup_duration):
85 85 self.path = path
86 86 self.wakeup_duration = wakeup_duration
87 87
88 88 def __call__(self, ip, filename, lineno, columnno):
89 89 filename = find_filename(filename)
90 90
91 91 if not filename:
92 92 return
93 93
94 94 run_command(self.path, 'gvim', '--remote-silent +%d "%s"' % (lineno, filename))
95 95
96 96 sleep(self.wakeup_duration)
97 97
98 98 restore_console_focus()
99 99
100 100
101 101 def gvim(path = r"C:\Program Files\vim\vim71", wakeup_duration = 100):
102 102 synchronize_with_editor = GVimHook(path, wakeup_duration)
103 103 set_hook(synchronize_with_editor)
104 104
105 105
106 106 class EmacsHook:
107 107 def __init__(self, path, wakeup_duration, start_duration):
108 108 self.path = path
109 109 self.wakeup_duration = wakeup_duration
110 110 self.start_duration = start_duration
111 111
112 112 def __call__(self, ip, filename, lineno, columnno):
113 113 filename = find_filename(filename)
114 114
115 115 if not filename:
116 116 return
117 117
118 118 r = run_command(self.path, "emacsclient", '-n +%d:%d "%s" 2>nul' % (lineno, columnno, filename), False)
119 119 if r != 0:
120 120 run_command(self.path, 'runemacs', '--quick -f server-start +%d:%d "%s"' % (lineno, columnno, filename))
121 121 sleep(self.start_duration)
122 122 else:
123 123 sleep(self.wakeup_duration)
124 124
125 125 restore_console_focus()
126 126
127 127
128 128 def emacs(path = r"C:\Program Files\emacs\bin", wakeup_duration = 100, start_duration = 2000):
129 129 synchronize_with_editor = EmacsHook(path, wakeup_duration, start_duration)
130 130 set_hook(synchronize_with_editor)
131 131
132 132
133 133 class SciteHook:
134 134 def __init__(self, path, wakeup_duration, start_duration):
135 135 self.path = path
136 136 self.wakeup_duration = wakeup_duration
137 137 self.start_duration = start_duration
138 138
139 139 def __call__(self, ip, filename, lineno, columnno):
140 140 filename = find_filename(filename)
141 141
142 142 if not filename:
143 143 return
144 144
145 145 scites = scitedirector.findWindows()
146 146 if not scites:
147 147 run_command(self.path, "scite", '"-open:%s" -goto:%d' % (filename.replace("\\", "/"), lineno))
148 148
149 149 sleep(self.start_duration)
150 150 restore_console_focus()
151 151 else:
152 152 scite = scites[0]
153 153 scitedirector.sendCommand(scite, 'open:%s' % filename.replace("\\", "/"))
154 154 scitedirector.sendCommand(scite, "goto:%d" % lineno)
155 155
156 156
157 157 def scite(path = r"C:\Program Files\SciTE Source Code Editor", wakeup_duration = 100, start_duration = 500):
158 158 synchronize_with_editor = SciteHook(path, wakeup_duration, start_duration)
159 159 set_hook(synchronize_with_editor)
160 160
161 161
162 162 class NodePadPlusPlusHook:
163 163 def __init__(self, path, wakeup_duration):
164 164 self.path = path
165 165 self.wakeup_duration = wakeup_duration
166 166
167 167 def __call__(self, ip, filename, lineno, columnno):
168 168 filename = find_filename(filename)
169 169
170 170 if not filename:
171 171 return
172 172
173 173 run_command(self.path, "notepad++", '"%s" -n%d' % (filename, lineno))
174 174
175 175 sleep(self.wakeup_duration)
176 176
177 177 restore_console_focus()
178 178
179 179
180 180 def notepadplusplus(path = r"C:\Program Files\Notepad++", wakeup_duration = 100):
181 181 synchronize_with_editor = NodePadPlusPlusHook(path, wakeup_duration)
182 182 set_hook(synchronize_with_editor)
183 183
184 184
185 185 class PsPadHook:
186 186 def __init__(self, path, wakeup_duration):
187 187 self.path = path
188 188 self.wakeup_duration = wakeup_duration
189 189
190 190 def __call__(self, ip, filename, lineno, columnno):
191 191 filename = find_filename(filename)
192 192
193 193 if not filename:
194 194 return
195 195
196 196 run_command(self.path, "pspad", '"%s" -%d' % (filename, lineno))
197 197
198 198 sleep(self.wakeup_duration)
199 199
200 200 restore_console_focus()
201 201
202 202
203 203 def pspad(path = r"C:\Program Files\PSPad editor", wakeup_duration = 100):
204 204 synchronize_with_editor = PsPadHook(path, wakeup_duration)
205 205 set_hook(synchronize_with_editor)
206 206
207 207
208 208 # This is an example of DDE hook:
209 209 class UltraEditHook:
210 210 def __init__(self, path, wakeup_duration, start_duration):
211 211 self.path = path
212 212 self.wakeup_duration = wakeup_duration
213 213 self.start_duration = start_duration
214 214
215 215 def __call__(self, ip, filename, lineno, columnno):
216 216 filename = find_filename(filename)
217 217
218 218 if not filename:
219 219 return
220 220
221 221 server = dde.CreateServer()
222 222 server.Create("myddeserver")
223 223 conversation = dde.CreateConversation(server)
224 224 try:
225 225 conversation.ConnectTo("uedit32", "System")
226 226 conversation.Exec(r'[open("%s/%d"])' % (filename, lineno))
227 227
228 228 sleep(self.wakeup_duration)
229 229 except:
230 230 run_command(self.path, 'uedit32', '"%s/%d"' % (filename, lineno))
231 231
232 232 sleep(self.start_duration)
233 233
234 234 server.Shutdown()
235 235
236 236 restore_console_focus()
237 237
238 238
239 239 def ultraedit(path = r"C:\Program Files\IDM Computer Solutions\UltraEdit-32", wakeup_duration = 10, start_duration = 2000):
240 240 synchronize_with_editor = UltraEditHook(path, wakeup_duration, start_duration)
241 241 set_hook(synchronize_with_editor)
242 242 No newline at end of file
@@ -1,24 +1,24 b''
1 1 """ System wide configuration file for IPython.
2 2
3 3 This will be imported by ipython for all users.
4 4
5 5 After this ipy_user_conf.py is imported, user specific configuration
6 6 should reside there.
7 7
8 8 """
9 9
10 import IPython.ipapi
11 ip = IPython.ipapi.get()
10 from IPython.core import ipapi
11 ip = ipapi.get()
12 12
13 13 # add system wide configuration information, import extensions etc. here.
14 14 # nothing here is essential
15 15
16 16 import sys
17 17
18 18 import ext_rescapture # var = !ls and var = %magic
19 19 import pspersistence # %store magic
20 20 import clearcmd # %clear
21 21
22 22 import ipy_stock_completers
23 23
24 24 ip.load('IPython.core.history')
@@ -1,184 +1,184 b''
1 1 """Traits-aware tab completion.
2 2
3 3 This module provides a custom tab-completer that intelligently hides the names
4 4 that the enthought.traits library (http://code.enthought.com/traits)
5 5 automatically adds to all objects that inherit from its base HasTraits class.
6 6
7 7
8 8 Activation
9 9 ==========
10 10
11 11 To use this, put in your ~/.ipython/ipy_user_conf.py file:
12 12
13 13 from ipy_traits_completer import activate
14 14 activate([complete_threshold])
15 15
16 16 The optional complete_threshold argument is the minimal length of text you need
17 17 to type for tab-completion to list names that are automatically generated by
18 18 traits. The default value is 3. Note that at runtime, you can change this
19 19 value simply by doing:
20 20
21 21 import ipy_traits_completer
22 22 ipy_traits_completer.COMPLETE_THRESHOLD = 4
23 23
24 24
25 25 Usage
26 26 =====
27 27
28 28 The system works as follows. If t is an empty object that HasTraits, then
29 29 (assuming the threshold is at the default value of 3):
30 30
31 31 In [7]: t.ed<TAB>
32 32
33 33 doesn't show anything at all, but:
34 34
35 35 In [7]: t.edi<TAB>
36 36 t.edit_traits t.editable_traits
37 37
38 38 shows these two names that come from traits. This allows you to complete on
39 39 the traits-specific names by typing at least 3 letters from them (or whatever
40 40 you set your threshold to), but to otherwise not see them in normal completion.
41 41
42 42
43 43 Notes
44 44 =====
45 45
46 46 - This requires Python 2.4 to work (I use sets). I don't think anyone is
47 47 using traits with 2.3 anyway, so that's OK.
48 48 """
49 49
50 50 #############################################################################
51 51 # External imports
52 52 from enthought.traits import api as T
53 53
54 54 # IPython imports
55 from IPython.ipapi import TryNext, get as ipget
55 from IPython.core.ipapi import TryNext, get as ipget
56 56 from IPython.utils.genutils import dir2
57 57 try:
58 58 set
59 59 except:
60 60 from sets import Set as set
61 61
62 62 #############################################################################
63 63 # Module constants
64 64
65 65 # The completion threshold
66 66 # This is currently implemented as a module global, since this sytem isn't
67 67 # likely to be modified at runtime by multiple instances. If needed in the
68 68 # future, we can always make it local to the completer as a function attribute.
69 69 COMPLETE_THRESHOLD = 3
70 70
71 71 # Set of names that Traits automatically adds to ANY traits-inheriting object.
72 72 # These are the names we'll filter out.
73 73 TRAIT_NAMES = set( dir2(T.HasTraits()) ) - set( dir2(object()) )
74 74
75 75 #############################################################################
76 76 # Code begins
77 77
78 78 def trait_completer(self,event):
79 79 """A custom IPython tab-completer that is traits-aware.
80 80
81 81 It tries to hide the internal traits attributes, and reveal them only when
82 82 it can reasonably guess that the user really is after one of them.
83 83 """
84 84
85 85 #print '\nevent is:',event # dbg
86 86 symbol_parts = event.symbol.split('.')
87 87 base = '.'.join(symbol_parts[:-1])
88 88 #print 'base:',base # dbg
89 89
90 90 oinfo = self._ofind(base)
91 91 if not oinfo['found']:
92 92 raise TryNext
93 93
94 94 obj = oinfo['obj']
95 95 # OK, we got the object. See if it's traits, else punt
96 96 if not isinstance(obj,T.HasTraits):
97 97 raise TryNext
98 98
99 99 # it's a traits object, don't show the tr* attributes unless the completion
100 100 # begins with 'tr'
101 101 attrs = dir2(obj)
102 102 # Now, filter out the attributes that start with the user's request
103 103 attr_start = symbol_parts[-1]
104 104 if attr_start:
105 105 attrs = [a for a in attrs if a.startswith(attr_start)]
106 106
107 107 # Let's also respect the user's readline_omit__names setting:
108 108 omit__names = ipget().options.readline_omit__names
109 109 if omit__names == 1:
110 110 attrs = [a for a in attrs if not a.startswith('__')]
111 111 elif omit__names == 2:
112 112 attrs = [a for a in attrs if not a.startswith('_')]
113 113
114 114 #print '\nastart:<%r>' % attr_start # dbg
115 115
116 116 if len(attr_start)<COMPLETE_THRESHOLD:
117 117 attrs = list(set(attrs) - TRAIT_NAMES)
118 118
119 119 # The base of the completion, so we can form the final results list
120 120 bdot = base+'.'
121 121
122 122 tcomp = [bdot+a for a in attrs]
123 123 #print 'tcomp:',tcomp
124 124 return tcomp
125 125
126 126 def activate(complete_threshold = COMPLETE_THRESHOLD):
127 127 """Activate the Traits completer.
128 128
129 129 :Keywords:
130 130 complete_threshold : int
131 131 The minimum number of letters that a user must type in order to
132 132 activate completion of traits-private names."""
133 133
134 134 if not (isinstance(complete_threshold,int) and
135 135 complete_threshold>0):
136 136 e='complete_threshold must be a positive integer, not %r' % \
137 137 complete_threshold
138 138 raise ValueError(e)
139 139
140 140 # Set the module global
141 141 global COMPLETE_THRESHOLD
142 142 COMPLETE_THRESHOLD = complete_threshold
143 143
144 144 # Activate the traits aware completer
145 145 ip = ipget()
146 146 ip.set_hook('complete_command', trait_completer, re_key = '.*')
147 147
148 148
149 149 #############################################################################
150 150 if __name__ == '__main__':
151 151 # Testing/debugging
152 152
153 153 # A sorted list of the names we'll filter out
154 154 TNL = list(TRAIT_NAMES)
155 155 TNL.sort()
156 156
157 157 # Make a few objects for testing
158 158 class TClean(T.HasTraits): pass
159 159 class Bunch(object): pass
160 160 # A clean traits object
161 161 t = TClean()
162 162 # A nested object containing t
163 163 f = Bunch()
164 164 f.t = t
165 165 # And a naked new-style object
166 166 o = object()
167 167
168 168 ip = ipget().IP
169 169
170 170 # A few simplistic tests
171 171
172 172 # Reset the threshold to the default, in case the test is running inside an
173 173 # instance of ipython that changed it
174 174 import ipy_traits_completer
175 175 ipy_traits_completer.COMPLETE_THRESHOLD = 3
176 176
177 177 assert ip.complete('t.ed') ==[]
178 178
179 179 # For some bizarre reason, these fail on the first time I run them, but not
180 180 # afterwards. Traits does some really weird stuff at object instantiation
181 181 # time...
182 182 ta = ip.complete('t.edi')
183 183 assert ta == ['t.edit_traits', 't.editable_traits']
184 184 print 'Tests OK'
@@ -1,239 +1,239 b''
1 1 """ Integration with gvim, by Erich Heine
2 2
3 3 Provides a %vim magic command, and reuses the same vim session. Uses
4 4 unix domain sockets for communication between vim and IPython. ipy.vim is
5 5 available in doc/examples of the IPython distribution.
6 6
7 7 Slightly touched up email announcement (and description how to use it) by
8 8 Erich Heine is here:
9 9
10 10 Ive recently been playing with ipython, and like it quite a bit. I did
11 11 however discover a bit of frustration, namely with editor interaction.
12 12 I am a gvim user, and using the command edit on a new file causes
13 13 ipython to try and run that file as soon as the text editor opens
14 14 up. The -x command of course fixes this, but its still a bit annoying,
15 15 switching windows to do a run file, then back to the text
16 16 editor. Being a heavy tab user in gvim, another annoyance is not being
17 17 able to specify weather a new tab is how I choose to open the file.
18 18
19 19 Not being one to shirk my open source duties (and seeing this as a
20 20 good excuse to poke around ipython internals), Ive created a script
21 21 for having gvim and ipython work very nicely together. Ive attached
22 22 both to this email (hoping of course that the mailing list allows such
23 23 things).
24 24
25 25 There are 2 files:
26 26
27 27 ipy_vimserver.py -- this file contains the ipython stuff
28 28 ipy.vim -- this file contains the gvim stuff
29 29
30 30 In combination they allow for a few functionalities:
31 31
32 32 #1. the vim magic command. This is a fancy wrapper around the edit
33 33 magic, that allows for a new option, -t, which opens the text in a new
34 34 gvim tab. Otherwise it works the same as edit -x. (it internally
35 35 calls edit -x). This magic command also juggles vim server management,
36 36 so when it is called when there is not a gvim running, it creates a
37 37 new gvim instance, named after the ipython session name. Once such a
38 38 gvim instance is running, it will be used for subsequent uses of the
39 39 vim command.
40 40
41 41 #2. ipython - gvim interaction. Once a file has been opened with the
42 42 vim magic (and a session set up, see below), pressing the F5 key in
43 43 vim will cause the calling ipython instance to execute run
44 44 filename.py. (if you typo like I do, this is very useful)
45 45
46 46 #3. ipython server - this is a thread wich listens on a unix domain
47 47 socket, and runs commands sent to that socket.
48 48
49 49 Note, this only works on POSIX systems, that allow for AF_UNIX type
50 50 sockets. It has only been tested on linux (a fairly recent debian
51 51 testing distro).
52 52
53 53 To install it put, the ipserver.py in your favorite locaion for
54 54 sourcing ipython scripts. I put the ipy.vim in
55 55 ~/.vim/after/ftplugin/python/.
56 56
57 57 To use (this can be scripted im sure, but i usually have 2 or 3
58 58 ipythons and corresponding gvims open):
59 59
60 60 import ipy_vimserver
61 61 ipy_vimserver.setup('sessionname')
62 62
63 63 (Editors note - you can probably add these to your ipy_user_conf.py)
64 64
65 65 Then use ipython as you normally would, until you need to edit
66 66 something. Instead of edit, use the vim magic. Thats it!
67 67
68 68 """
69 69
70 import IPython.ipapi
71 #import ipythonhooks
70 from IPython.core import ipapi
71
72 72 import socket, select
73 73 import os, threading, subprocess
74 74 import re
75 75
76 76 ERRCONDS = select.POLLHUP|select.POLLERR
77 77 SERVER = None
78 ip = IPython.ipapi.get()
78 ip = ipapi.get()
79 79
80 80 # this listens to a unix domain socket in a separate thread, so that comms
81 81 # between a vim instance and ipython can happen in a fun and productive way
82 82 class IpyServer(threading.Thread):
83 83 def __init__(self, sname):
84 84 super(IpyServer, self).__init__()
85 85 self.keep_running = True
86 86 self.__sname = sname
87 87 self.socket = socket.socket(socket.AF_UNIX)
88 88 self.poller = select.poll()
89 89 self.current_conns = dict()
90 90 self.setDaemon(True)
91 91
92 92 def listen(self):
93 93 self.socket.bind(self.__sname)
94 94 self.socket.listen(1)
95 95
96 96 def __handle_error(self, socket):
97 97 if socket == self.socket.fileno():
98 98 self.keep_running = False
99 99 for a in self.current_conns.values():
100 100 a.close()
101 101 return False
102 102 else:
103 103 y = self.current_conns[socket]
104 104 del self.current_conns[socket]
105 105 y.close()
106 106 self.poller.unregister(socket)
107 107
108 108 def serve_me(self):
109 109 self.listen()
110 110 self.poller.register(self.socket,select.POLLIN|ERRCONDS)
111 111
112 112 while self.keep_running:
113 113 try:
114 114 avail = self.poller.poll(1)
115 115 except:
116 116 continue
117 117
118 118 if not avail: continue
119 119
120 120 for sock, conds in avail:
121 121 if conds & (ERRCONDS):
122 122 if self.__handle_error(sock): continue
123 123 else: break
124 124
125 125 if sock == self.socket.fileno():
126 126 y = self.socket.accept()[0]
127 127 self.poller.register(y, select.POLLIN|ERRCONDS)
128 128 self.current_conns[y.fileno()] = y
129 129 else: y = self.current_conns.get(sock)
130 130
131 131 self.handle_request(y)
132 132
133 133 os.remove(self.__sname)
134 134
135 135 run = serve_me
136 136
137 137 def stop(self):
138 138 self.keep_running = False
139 139
140 140 def handle_request(self,sock):
141 141 sock.settimeout(1)
142 142 while self.keep_running:
143 143 try:
144 144 x = sock.recv(4096)
145 145 except socket.timeout:
146 146 pass
147 147 else:
148 148 break
149 149 self.do_it(x)
150 150
151 151 def do_it(self, data):
152 152 data = data.split('\n')
153 153 cmds = list()
154 154 for line in data:
155 155 cmds.append(line)
156 156 ip.runlines(cmds)
157 157
158 158
159 159 # try to help ensure that the unix domain socket is cleaned up proper
160 160 def shutdown_server(self):
161 161 if SERVER:
162 162 SERVER.stop()
163 163 SERVER.join(3)
164 raise IPython.ipapi.TryNext
164 raise ipapi.TryNext
165 165
166 166 ip.set_hook('shutdown_hook', shutdown_server, 10)
167 167
168 168 # this fun function exists to make setup easier for all, and makes the
169 169 # vimhook function ready for instance specific communication
170 170 def setup(sessionname='',socketdir=os.path.expanduser('~/.ipython/')):
171 171 global SERVER
172 172
173 173 if sessionname:
174 174 session = sessionname
175 175 elif os.environ.get('IPY_SESSION'):
176 176 session = os.environ.get('IPY_SESSION')
177 177 else:
178 178 session = 'IPYS'
179 179 vimhook.vimserver=session
180 180 vimhook.ipyserver = os.path.join(socketdir, session)
181 181 if not SERVER:
182 182 SERVER = IpyServer(vimhook.ipyserver)
183 183 SERVER.start()
184 184
185 185
186 186
187 187 # calls gvim, with all ops happening on the correct gvim instance for this
188 188 # ipython instance. it then calls edit -x (since gvim will return right away)
189 189 # things of note: it sets up a special environment, so that the ipy.vim script
190 190 # can connect back to the ipython instance and do fun things, like run the file
191 191 def vimhook(self, fname, line):
192 192 env = os.environ.copy()
193 193 vserver = vimhook.vimserver.upper()
194 194 check = subprocess.Popen('gvim --serverlist', stdout = subprocess.PIPE,
195 195 shell=True)
196 196 check.wait()
197 197 cval = [l for l in check.stdout.readlines() if vserver in l]
198 198
199 199 if cval:
200 200 vimargs = '--remote%s' % (vimhook.extras,)
201 201 else:
202 202 vimargs = ''
203 203 vimhook.extras = ''
204 204
205 205 env['IPY_SESSION'] = vimhook.vimserver
206 206 env['IPY_SERVER'] = vimhook.ipyserver
207 207
208 208 if line is None: line = ''
209 209 else: line = '+' + line
210 210 vim_cmd = 'gvim --servername %s %s %s %s' % (vimhook.vimserver, vimargs,
211 211 line, fname)
212 212 subprocess.call(vim_cmd, env=env, shell=True)
213 213
214 214
215 215 #default values to keep it sane...
216 216 vimhook.vimserver = ''
217 217 vimhook.ipyserver = ''
218 218
219 219 ip.set_hook('editor',vimhook)
220 220
221 221 # this is set up so more vim specific commands can be added, instead of just
222 222 # the current -t. all thats required is a compiled regex, a call to do_arg(pat)
223 223 # and the logic to deal with the new feature
224 224 newtab = re.compile(r'-t(?:\s|$)')
225 225 def vim(self, argstr):
226 226 def do_arg(pat, rarg):
227 227 x = len(pat.findall(argstr))
228 228 if x:
229 229 a = pat.sub('',argstr)
230 230 return rarg, a
231 231 else: return '', argstr
232 232
233 233 t, argstr = do_arg(newtab, '-tab')
234 234 vimhook.extras = t
235 235 argstr = 'edit -x ' + argstr
236 236 ip.magic(argstr)
237 237
238 238 ip.expose_magic('vim', vim)
239 239
@@ -1,76 +1,76 b''
1 1 r""" %which magic command
2 2
3 3 %which <cmd> => search PATH for files matching PATH. Also scans aliases
4 4
5 5 """
6 6
7 import IPython.ipapi
8 ip = IPython.ipapi.get()
7 from IPython.core import ipapi
8 ip = ipapi.get()
9 9
10 10 import os,sys
11 11 from fnmatch import fnmatch
12 12 def which(fname):
13 13 fullpath = filter(os.path.isdir,os.environ['PATH'].split(os.pathsep))
14 14
15 15 if '.' not in fullpath:
16 16 fullpath = ['.'] + fullpath
17 17 fn = fname
18 18 for p in fullpath:
19 19 for f in os.listdir(p):
20 20 head, ext = os.path.splitext(f)
21 21 if f == fn or fnmatch(head, fn):
22 22 yield os.path.join(p,f)
23 23 return
24 24
25 25 def which_alias(fname):
26 26 for al, tgt in ip.IP.alias_table.items():
27 27 if not (al == fname or fnmatch(al, fname)):
28 28 continue
29 29 if callable(tgt):
30 30 print "Callable alias",tgt
31 31 d = tgt.__doc__
32 32 if d:
33 33 print "Docstring:\n",d
34 34 continue
35 35 trg = tgt[1]
36 36
37 37 trans = ip.expand_alias(trg)
38 38 cmd = trans.split(None,1)[0]
39 39 print al,"->",trans
40 40 for realcmd in which(cmd):
41 41 print " ==",realcmd
42 42
43 43 def which_f(self, arg):
44 44 r""" %which <cmd> => search PATH for files matching cmd. Also scans aliases.
45 45
46 46 Traverses PATH and prints all files (not just executables!) that match the
47 47 pattern on command line. Probably more useful in finding stuff
48 48 interactively than 'which', which only prints the first matching item.
49 49
50 50 Also discovers and expands aliases, so you'll see what will be executed
51 51 when you call an alias.
52 52
53 53 Example:
54 54
55 55 [~]|62> %which d
56 56 d -> ls -F --color=auto
57 57 == c:\cygwin\bin\ls.exe
58 58 c:\cygwin\bin\d.exe
59 59
60 60 [~]|64> %which diff*
61 61 diff3 -> diff3
62 62 == c:\cygwin\bin\diff3.exe
63 63 diff -> diff
64 64 == c:\cygwin\bin\diff.exe
65 65 c:\cygwin\bin\diff.exe
66 66 c:\cygwin\bin\diff3.exe
67 67
68 68 """
69 69
70 70 which_alias(arg)
71 71
72 72 for e in which(arg):
73 73 print e
74 74
75 75 ip.expose_magic("which",which_f)
76 76
@@ -1,83 +1,83 b''
1 1 """ Debug a script (like %run -d) in IPython process, Using WinPdb
2 2
3 3 Usage:
4 4
5 5 %wdb test.py
6 6 run test.py, with a winpdb breakpoint at start of the file
7 7
8 8 %wdb pass
9 9 Change the password (e.g. if you have forgotten the old one)
10 10
11 11
12 12 Notes
13 13 -----
14 14
15 15 **WARNING**: As of March 2009 (IPython 0.10), WinPdb has a known bug, which
16 16 causes PyTables to become impossible to import if winpdb is loaded. Therefore,
17 17 if you need PyTables, do *not* use this extension.
18 18
19 19 For more details: https://bugs.launchpad.net/ipython/+bug/249036
20 20 """
21 21
22 22 import os
23 23
24 import IPython.ipapi
24 from IPython.core import ipapi
25 25 import rpdb2
26 26
27 ip = IPython.ipapi.get()
27 ip = ipapi.get()
28 28
29 29 rpdb_started = False
30 30
31 31 def wdb_f(self, arg):
32 32 """ Debug a script (like %run -d) in IPython process, Using WinPdb
33 33
34 34 Usage:
35 35
36 36 %wdb test.py
37 37 run test.py, with a winpdb breakpoint at start of the file
38 38
39 39 %wdb pass
40 40 Change the password (e.g. if you have forgotten the old one)
41 41
42 42 Note that after the script has been run, you need to do "Go" (f5)
43 43 in WinPdb to resume normal IPython operation.
44 44 """
45 45
46 46 global rpdb_started
47 47 if not arg.strip():
48 48 print __doc__
49 49 return
50 50
51 51 if arg.strip() == 'pass':
52 52 passwd = raw_input('Enter new winpdb session password: ')
53 53 ip.db['winpdb_pass'] = passwd
54 54 print "Winpdb password changed"
55 55 if rpdb_started:
56 56 print "You need to restart IPython to use the new password"
57 57 return
58 58
59 59 path = os.path.abspath(arg)
60 60 if not os.path.isfile(path):
61 raise IPython.ipapi.UsageError("%%wdb: file %s does not exist" % path)
61 raise ipapi.UsageError("%%wdb: file %s does not exist" % path)
62 62 if not rpdb_started:
63 63 passwd = ip.db.get('winpdb_pass', None)
64 64 if passwd is None:
65 65 import textwrap
66 66 print textwrap.dedent("""\
67 67 Winpdb sessions need a password that you use for attaching the external
68 68 winpdb session. IPython will remember this. You can change the password later
69 69 by '%wpdb pass'
70 70 """)
71 71 passwd = raw_input('Enter new winpdb session password: ')
72 72 ip.db['winpdb_pass'] = passwd
73 73
74 74 print "Starting rpdb2 in IPython process"
75 75 rpdb2.start_embedded_debugger(passwd, timeout = 0)
76 76 rpdb_started = True
77 77
78 78 rpdb2.set_temp_breakpoint(path)
79 79 print 'It is time to attach with WinPdb (launch WinPdb if needed, File -> Attach)'
80 80 ip.magic('%run ' + arg)
81 81
82 82
83 83 ip.expose_magic('wdb', wdb_f)
@@ -1,43 +1,43 b''
1 1 #!/usr/bin/env python
2 2
3 import IPython.ipapi
4 ip = IPython.ipapi.get()
3 from IPython.core import ipapi
4 ip = ipapi.get()
5 5
6 6 import os, subprocess
7 7
8 8 workdir = None
9 9 def workdir_f(ip,line):
10 10 """ Exceute commands residing in cwd elsewhere
11 11
12 12 Example::
13 13
14 14 workdir /myfiles
15 15 cd bin
16 16 workdir myscript.py
17 17
18 18 executes myscript.py (stored in bin, but not in path) in /myfiles
19 19 """
20 20 global workdir
21 21 dummy,cmd = line.split(None,1)
22 22 if os.path.isdir(cmd):
23 23 workdir = os.path.abspath(cmd)
24 24 print "Set workdir",workdir
25 25 elif workdir is None:
26 26 print "Please set workdir first by doing e.g. 'workdir q:/'"
27 27 else:
28 28 sp = cmd.split(None,1)
29 29 if len(sp) == 1:
30 30 head, tail = cmd, ''
31 31 else:
32 32 head, tail = sp
33 33 if os.path.isfile(head):
34 34 cmd = os.path.abspath(head) + ' ' + tail
35 35 print "Execute command '" + cmd+ "' in",workdir
36 36 olddir = os.getcwd()
37 37 os.chdir(workdir)
38 38 try:
39 39 os.system(cmd)
40 40 finally:
41 41 os.chdir(olddir)
42 42
43 43 ip.defalias("workdir",workdir_f)
@@ -1,242 +1,242 b''
1 1 """ Preliminary "job control" extensions for IPython
2 2
3 3 requires python 2.4 (or separate 'subprocess' module
4 4
5 5 This provides 2 features, launching background jobs and killing foreground jobs from another IPython instance.
6 6
7 7 Launching background jobs:
8 8
9 9 Usage:
10 10
11 11 [ipython]|2> import jobctrl
12 12 [ipython]|3> &ls
13 13 <3> <jobctrl.IpyPopen object at 0x00D87FD0>
14 14 [ipython]|4> _3.go
15 15 -----------> _3.go()
16 16 ChangeLog
17 17 IPython
18 18 MANIFEST.in
19 19 README
20 20 README_Windows.txt
21 21
22 22 ...
23 23
24 24 Killing foreground tasks:
25 25
26 26 Launch IPython instance, run a blocking command:
27 27
28 28 [Q:/ipython]|1> import jobctrl
29 29 [Q:/ipython]|2> cat
30 30
31 31 Now launch a new IPython prompt and kill the process:
32 32
33 33 IPython 0.8.3.svn.r2919 [on Py 2.5]
34 34 [Q:/ipython]|1> import jobctrl
35 35 [Q:/ipython]|2> %tasks
36 36 6020: 'cat ' (Q:\ipython)
37 37 [Q:/ipython]|3> %kill
38 38 SUCCESS: The process with PID 6020 has been terminated.
39 39 [Q:/ipython]|4>
40 40
41 41 (you don't need to specify PID for %kill if only one task is running)
42 42 """
43 43
44 44 from subprocess import *
45 45 import os,shlex,sys,time
46 46 import threading,Queue
47 47
48 48 from IPython.utils import genutils
49 49
50 import IPython.ipapi
50 from IPython.core import ipapi
51 51
52 52 if os.name == 'nt':
53 53 def kill_process(pid):
54 54 os.system('taskkill /F /PID %d' % pid)
55 55 else:
56 56 def kill_process(pid):
57 57 os.system('kill -9 %d' % pid)
58 58
59 59
60 60
61 61 class IpyPopen(Popen):
62 62 def go(self):
63 63 print self.communicate()[0]
64 64 def __repr__(self):
65 65 return '<IPython job "%s" PID=%d>' % (self.line, self.pid)
66 66
67 67 def kill(self):
68 68 kill_process(self.pid)
69 69
70 70 def startjob(job):
71 71 p = IpyPopen(shlex.split(job), stdout=PIPE, shell = False)
72 72 p.line = job
73 73 return p
74 74
75 75 class AsyncJobQ(threading.Thread):
76 76 def __init__(self):
77 77 threading.Thread.__init__(self)
78 78 self.q = Queue.Queue()
79 79 self.output = []
80 80 self.stop = False
81 81 def run(self):
82 82 while 1:
83 83 cmd,cwd = self.q.get()
84 84 if self.stop:
85 85 self.output.append("** Discarding: '%s' - %s" % (cmd,cwd))
86 86 continue
87 87 self.output.append("** Task started: '%s' - %s" % (cmd,cwd))
88 88
89 89 p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd = cwd)
90 90 out = p.stdout.read()
91 91 self.output.append("** Task complete: '%s'\n" % cmd)
92 92 self.output.append(out)
93 93
94 94 def add(self,cmd):
95 95 self.q.put_nowait((cmd, os.getcwd()))
96 96
97 97 def dumpoutput(self):
98 98 while self.output:
99 99 item = self.output.pop(0)
100 100 print item
101 101
102 102 _jobq = None
103 103
104 104 def jobqueue_f(self, line):
105 105
106 106 global _jobq
107 107 if not _jobq:
108 108 print "Starting jobqueue - do '&some_long_lasting_system_command' to enqueue"
109 109 _jobq = AsyncJobQ()
110 110 _jobq.setDaemon(True)
111 111 _jobq.start()
112 112 ip.jobq = _jobq.add
113 113 return
114 114 if line.strip() == 'stop':
115 115 print "Stopping and clearing jobqueue, %jobqueue start to start again"
116 116 _jobq.stop = True
117 117 return
118 118 if line.strip() == 'start':
119 119 _jobq.stop = False
120 120 return
121 121
122 122 def jobctrl_prefilter_f(self,line):
123 123 if line.startswith('&'):
124 124 pre,fn,rest = self.split_user_input(line[1:])
125 125
126 126 line = ip.IP.expand_aliases(fn,rest)
127 127 if not _jobq:
128 128 return '_ip.startjob(%s)' % genutils.make_quoted_expr(line)
129 129 return '_ip.jobq(%s)' % genutils.make_quoted_expr(line)
130 130
131 raise IPython.ipapi.TryNext
131 raise ipapi.TryNext
132 132
133 133 def jobq_output_hook(self):
134 134 if not _jobq:
135 135 return
136 136 _jobq.dumpoutput()
137 137
138 138
139 139
140 140 def job_list(ip):
141 141 keys = ip.db.keys('tasks/*')
142 142 ents = [ip.db[k] for k in keys]
143 143 return ents
144 144
145 145 def magic_tasks(self,line):
146 146 """ Show a list of tasks.
147 147
148 148 A 'task' is a process that has been started in IPython when 'jobctrl' extension is enabled.
149 149 Tasks can be killed with %kill.
150 150
151 151 '%tasks clear' clears the task list (from stale tasks)
152 152 """
153 153 ip = self.getapi()
154 154 if line.strip() == 'clear':
155 155 for k in ip.db.keys('tasks/*'):
156 156 print "Clearing",ip.db[k]
157 157 del ip.db[k]
158 158 return
159 159
160 160 ents = job_list(ip)
161 161 if not ents:
162 162 print "No tasks running"
163 163 for pid,cmd,cwd,t in ents:
164 164 dur = int(time.time()-t)
165 165 print "%d: '%s' (%s) %d:%02d" % (pid,cmd,cwd, dur / 60,dur%60)
166 166
167 167 def magic_kill(self,line):
168 168 """ Kill a task
169 169
170 170 Without args, either kill one task (if only one running) or show list (if many)
171 171 With arg, assume it's the process id.
172 172
173 173 %kill is typically (much) more powerful than trying to terminate a process with ctrl+C.
174 174 """
175 175 ip = self.getapi()
176 176 jobs = job_list(ip)
177 177
178 178 if not line.strip():
179 179 if len(jobs) == 1:
180 180 kill_process(jobs[0][0])
181 181 else:
182 182 magic_tasks(self,line)
183 183 return
184 184
185 185 try:
186 186 pid = int(line)
187 187 kill_process(pid)
188 188 except ValueError:
189 189 magic_tasks(self,line)
190 190
191 191 if sys.platform == 'win32':
192 192 shell_internal_commands = 'break chcp cls copy ctty date del erase dir md mkdir path prompt rd rmdir start time type ver vol'.split()
193 193 PopenExc = WindowsError
194 194 else:
195 195 # todo linux commands
196 196 shell_internal_commands = []
197 197 PopenExc = OSError
198 198
199 199
200 200 def jobctrl_shellcmd(ip,cmd):
201 201 """ os.system replacement that stores process info to db['tasks/t1234'] """
202 202 cmd = cmd.strip()
203 203 cmdname = cmd.split(None,1)[0]
204 204 if cmdname in shell_internal_commands or '|' in cmd or '>' in cmd or '<' in cmd:
205 205 use_shell = True
206 206 else:
207 207 use_shell = False
208 208
209 209 jobentry = None
210 210 try:
211 211 try:
212 212 p = Popen(cmd,shell = use_shell)
213 213 except PopenExc :
214 214 if use_shell:
215 215 # try with os.system
216 216 os.system(cmd)
217 217 return
218 218 else:
219 219 # have to go via shell, sucks
220 220 p = Popen(cmd,shell = True)
221 221
222 222 jobentry = 'tasks/t' + str(p.pid)
223 223 ip.db[jobentry] = (p.pid,cmd,os.getcwd(),time.time())
224 224 p.communicate()
225 225
226 226 finally:
227 227 if jobentry:
228 228 del ip.db[jobentry]
229 229
230 230
231 231 def install():
232 232 global ip
233 ip = IPython.ipapi.get()
233 ip = ipapi.get()
234 234 # needed to make startjob visible as _ip.startjob('blah')
235 235 ip.startjob = startjob
236 236 ip.set_hook('input_prefilter', jobctrl_prefilter_f)
237 237 ip.set_hook('shell_hook', jobctrl_shellcmd)
238 238 ip.expose_magic('kill',magic_kill)
239 239 ip.expose_magic('tasks',magic_tasks)
240 240 ip.expose_magic('jobqueue',jobqueue_f)
241 241 ip.set_hook('pre_prompt_hook', jobq_output_hook)
242 242 install()
@@ -1,98 +1,98 b''
1 1 """ Fun magic line editor for ipython
2 2
3 3 Use this to easily edit lists of strings gradually without crafting long
4 4 list comprehensions.
5 5
6 6 'l' is the magic variable name for every line (array element). Save the current
7 7 result (or more exactly, retrieve the last ipython computation result into
8 8 %led work area) by running '%led s'. Just run '%led' to show the current work
9 9 area data.
10 10
11 11 Example use:
12 12
13 13 [ipython]|25> setups = !ls *setup*.py
14 14 ==
15 15 ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py']
16 16 [ipython]|26> setups
17 17 <26> ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py']
18 18 [ipython]|27> %led s
19 19 Data set from last result (_)
20 20 <27> ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py']
21 21 [ipython]|28> %led upper
22 22 cmd translated => l.upper()
23 23 <28> ['EGGSETUP.PY', 'SETUP.PY', 'SETUP_BDIST_EGG.PY']
24 24 [ipython]|29> %led
25 25 Magic line editor (for lists of strings)
26 26 current data is:
27 27 ['eggsetup.py', 'setup.py', 'setup_bdist_egg.py']
28 28 [ipython]|30> %led upper
29 29 cmd translated => l.upper()
30 30 <30> ['EGGSETUP.PY', 'SETUP.PY', 'SETUP_BDIST_EGG.PY']
31 31 [ipython]|31> %led s
32 32 Data set from last result (_)
33 33 <31> ['EGGSETUP.PY', 'SETUP.PY', 'SETUP_BDIST_EGG.PY']
34 34 [ipython]|32> %led "n:" + l
35 35 <32> ['n:EGGSETUP.PY', 'n:SETUP.PY', 'n:SETUP_BDIST_EGG.PY']
36 36 [ipython]|33> %led s
37 37 Data set from last result (_)
38 38 <33> ['n:EGGSETUP.PY', 'n:SETUP.PY', 'n:SETUP_BDIST_EGG.PY']
39 39 [ipython]|34> %led l.
40 40 l.__add__ l.__gt__ l.__reduce_ex__ l.endswith l.join l.rstrip
41 41 l.__class__ l.__hash__ l.__repr__ l.expandtabs l.ljust l.split
42 42
43 43 ... (completions for string variable shown ) ...
44 44
45 45 """
46 import IPython.ipapi
46 from IPython.core import ipapi
47 47 import pprint
48 ip = IPython.ipapi.get()
48 ip = ipapi.get()
49 49
50 50 curdata = []
51 51
52 52 def line_edit_f(self, cmd ):
53 53 global curdata
54 54
55 55 if not cmd:
56 56
57 57 print "Magic line editor (for lists of strings)"
58 58 if curdata:
59 59 print "current data is:"
60 60 pprint.pprint(curdata)
61 61 else:
62 62 print "No current data, you should set it by running '%led s'"
63 63 print "When you have your data in _ (result of last computation)."
64 64 return
65 65
66 66 if cmd == 's':
67 67 curdata = ip.ev('_')
68 68 print "Data set from last result (_)"
69 69 newlines = curdata
70 70
71 71 else:
72 72 # simple method call, e.g. upper
73 73 if cmd.isalpha():
74 74 cmd = 'l.' + cmd + '()'
75 75 print "cmd translated =>",cmd
76 76
77 77 newlines = []
78 78 for l in curdata:
79 79 try:
80 80 l2 = eval(cmd)
81 81 except Exception,e:
82 82 print "Dropping exception",e,"on line:",l
83 83 continue
84 84 newlines.append(l2)
85 85
86 86
87 87 return newlines
88 88
89 89 def line_edit_complete_f(self,event):
90 90 """ Show all string methods in completions """
91 91 if event.symbol.startswith('l.'):
92 92 return ['l.' + func for func in dir('')]
93 93
94 94 return dir('') + ['l.' + func for func in dir('')]
95 95
96 96 ip.set_hook('complete_command', line_edit_complete_f , str_key = '%led')
97 97
98 98 ip.expose_magic('led', line_edit_f) No newline at end of file
@@ -1,182 +1,182 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 %store magic for lightweight persistence.
4 4
5 5 Stores variables, aliases etc. in PickleShare database.
6 6 """
7 7
8 import IPython.ipapi
9 from IPython.ipapi import UsageError
10 ip = IPython.ipapi.get()
8 from IPython.core import ipapi
9 from IPython.core.ipapi import UsageError
10 ip = ipapi.get()
11 11
12 12 import pickleshare
13 13
14 14 import inspect,pickle,os,sys,textwrap
15 15 from IPython.core.fakemodule import FakeModule
16 16
17 17 def restore_aliases(self):
18 18 ip = self.getapi()
19 19 staliases = ip.db.get('stored_aliases', {})
20 20 for k,v in staliases.items():
21 21 #print "restore alias",k,v # dbg
22 22 #self.alias_table[k] = v
23 23 ip.defalias(k,v)
24 24
25 25
26 26 def refresh_variables(ip):
27 27 db = ip.db
28 28 for key in db.keys('autorestore/*'):
29 29 # strip autorestore
30 30 justkey = os.path.basename(key)
31 31 try:
32 32 obj = db[key]
33 33 except KeyError:
34 34 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
35 35 print "The error was:",sys.exc_info()[0]
36 36 else:
37 37 #print "restored",justkey,"=",obj #dbg
38 38 ip.user_ns[justkey] = obj
39 39
40 40
41 41 def restore_dhist(ip):
42 42 db = ip.db
43 43 ip.user_ns['_dh'] = db.get('dhist',[])
44 44
45 45 def restore_data(self):
46 46 ip = self.getapi()
47 47 refresh_variables(ip)
48 48 restore_aliases(self)
49 49 restore_dhist(self)
50 raise IPython.ipapi.TryNext
50 raise ipapi.TryNext
51 51
52 52 ip.set_hook('late_startup_hook', restore_data)
53 53
54 54 def magic_store(self, parameter_s=''):
55 55 """Lightweight persistence for python variables.
56 56
57 57 Example:
58 58
59 59 ville@badger[~]|1> A = ['hello',10,'world']\\
60 60 ville@badger[~]|2> %store A\\
61 61 ville@badger[~]|3> Exit
62 62
63 63 (IPython session is closed and started again...)
64 64
65 65 ville@badger:~$ ipython -p pysh\\
66 66 ville@badger[~]|1> print A
67 67
68 68 ['hello', 10, 'world']
69 69
70 70 Usage:
71 71
72 72 %store - Show list of all variables and their current values\\
73 73 %store <var> - Store the *current* value of the variable to disk\\
74 74 %store -d <var> - Remove the variable and its value from storage\\
75 75 %store -z - Remove all variables from storage\\
76 76 %store -r - Refresh all variables from store (delete current vals)\\
77 77 %store foo >a.txt - Store value of foo to new file a.txt\\
78 78 %store foo >>a.txt - Append value of foo to file a.txt\\
79 79
80 80 It should be noted that if you change the value of a variable, you
81 81 need to %store it again if you want to persist the new value.
82 82
83 83 Note also that the variables will need to be pickleable; most basic
84 84 python types can be safely %stored.
85 85
86 86 Also aliases can be %store'd across sessions.
87 87 """
88 88
89 89 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
90 90 args = argsl.split(None,1)
91 91 ip = self.getapi()
92 92 db = ip.db
93 93 # delete
94 94 if opts.has_key('d'):
95 95 try:
96 96 todel = args[0]
97 97 except IndexError:
98 98 raise UsageError('You must provide the variable to forget')
99 99 else:
100 100 try:
101 101 del db['autorestore/' + todel]
102 102 except:
103 103 raise UsageError("Can't delete variable '%s'" % todel)
104 104 # reset
105 105 elif opts.has_key('z'):
106 106 for k in db.keys('autorestore/*'):
107 107 del db[k]
108 108
109 109 elif opts.has_key('r'):
110 110 refresh_variables(ip)
111 111
112 112
113 113 # run without arguments -> list variables & values
114 114 elif not args:
115 115 vars = self.db.keys('autorestore/*')
116 116 vars.sort()
117 117 if vars:
118 118 size = max(map(len,vars))
119 119 else:
120 120 size = 0
121 121
122 122 print 'Stored variables and their in-db values:'
123 123 fmt = '%-'+str(size)+'s -> %s'
124 124 get = db.get
125 125 for var in vars:
126 126 justkey = os.path.basename(var)
127 127 # print 30 first characters from every var
128 128 print fmt % (justkey,repr(get(var,'<unavailable>'))[:50])
129 129
130 130 # default action - store the variable
131 131 else:
132 132 # %store foo >file.txt or >>file.txt
133 133 if len(args) > 1 and args[1].startswith('>'):
134 134 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
135 135 if args[1].startswith('>>'):
136 136 fil = open(fnam,'a')
137 137 else:
138 138 fil = open(fnam,'w')
139 139 obj = ip.ev(args[0])
140 140 print "Writing '%s' (%s) to file '%s'." % (args[0],
141 141 obj.__class__.__name__, fnam)
142 142
143 143
144 144 if not isinstance (obj,basestring):
145 145 from pprint import pprint
146 146 pprint(obj,fil)
147 147 else:
148 148 fil.write(obj)
149 149 if not obj.endswith('\n'):
150 150 fil.write('\n')
151 151
152 152 fil.close()
153 153 return
154 154
155 155 # %store foo
156 156 try:
157 157 obj = ip.user_ns[args[0]]
158 158 except KeyError:
159 159 # it might be an alias
160 160 if args[0] in self.alias_table:
161 161 staliases = db.get('stored_aliases',{})
162 162 staliases[ args[0] ] = self.alias_table[ args[0] ]
163 163 db['stored_aliases'] = staliases
164 164 print "Alias stored:", args[0], self.alias_table[ args[0] ]
165 165 return
166 166 else:
167 167 raise UsageError("Unknown variable '%s'" % args[0])
168 168
169 169 else:
170 170 if isinstance(inspect.getmodule(obj), FakeModule):
171 171 print textwrap.dedent("""\
172 172 Warning:%s is %s
173 173 Proper storage of interactively declared classes (or instances
174 174 of those classes) is not possible! Only instances
175 175 of classes in real modules on file system can be %%store'd.
176 176 """ % (args[0], obj) )
177 177 return
178 178 #pickled = pickle.dumps(obj)
179 179 self.db[ 'autorestore/' + args[0] ] = obj
180 180 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
181 181
182 182 ip.expose_magic('store',magic_store)
@@ -1,45 +1,45 b''
1 import IPython.ipapi
1 from IPython.core import ipapi
2 2
3 ip = IPython.ipapi.get()
3 ip = ipapi.get()
4 4
5 5 def clip_f( self, parameter_s = '' ):
6 6 """Save a set of lines to the clipboard.
7 7
8 8 Usage:\\
9 9 %clip n1-n2 n3-n4 ... n5 .. n6 ...
10 10
11 11 This function uses the same syntax as %macro for line extraction, but
12 12 instead of creating a macro it saves the resulting string to the
13 13 clipboard.
14 14
15 15 When used without arguments, this returns the text contents of the clipboard.
16 16 E.g.
17 17
18 18 mytext = %clip
19 19
20 20 """
21 21
22 22 import win32clipboard as cl
23 23 import win32con
24 24 args = parameter_s.split()
25 25 cl.OpenClipboard()
26 26 if len( args ) == 0:
27 27 data = cl.GetClipboardData( win32con.CF_TEXT )
28 28 cl.CloseClipboard()
29 29 return data
30 30 api = self.getapi()
31 31
32 32 if parameter_s.lstrip().startswith('='):
33 33 rest = parameter_s[parameter_s.index('=')+1:].strip()
34 34 val = str(api.ev(rest))
35 35 else:
36 36 ranges = args[0:]
37 37 val = ''.join( self.extract_input_slices( ranges ) )
38 38
39 39 cl.EmptyClipboard()
40 40 cl.SetClipboardText( val )
41 41 cl.CloseClipboard()
42 42 print 'The following text was written to the clipboard'
43 43 print val
44 44
45 45 ip.expose_magic( "clip", clip_f )
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,627 +1,627 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Classes for handling input/output prompts.
4 4 """
5 5
6 6 #*****************************************************************************
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 # Copyright (C) 2001-2007 Fernando Perez <fperez@colorado.edu>
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #*****************************************************************************
13 13
14 14 #****************************************************************************
15 15 # Required modules
16 16 import __builtin__
17 17 import os
18 18 import socket
19 19 import sys
20 20 import time
21 21
22 22 # IPython's own
23 23 from IPython.utils import coloransi
24 24 from IPython import Release
25 25 from IPython.external.Itpl import ItplNS
26 from IPython.ipapi import TryNext
26 from IPython.core.ipapi import TryNext
27 27 from IPython.ipstruct import Struct
28 28 from IPython.macro import Macro
29 29 import IPython.utils.generics
30 30
31 31 from IPython.utils.genutils import *
32 32
33 33 #****************************************************************************
34 34 #Color schemes for Prompts.
35 35
36 36 PromptColors = coloransi.ColorSchemeTable()
37 37 InputColors = coloransi.InputTermColors # just a shorthand
38 38 Colors = coloransi.TermColors # just a shorthand
39 39
40 40 PromptColors.add_scheme(coloransi.ColorScheme(
41 41 'NoColor',
42 42 in_prompt = InputColors.NoColor, # Input prompt
43 43 in_number = InputColors.NoColor, # Input prompt number
44 44 in_prompt2 = InputColors.NoColor, # Continuation prompt
45 45 in_normal = InputColors.NoColor, # color off (usu. Colors.Normal)
46 46
47 47 out_prompt = Colors.NoColor, # Output prompt
48 48 out_number = Colors.NoColor, # Output prompt number
49 49
50 50 normal = Colors.NoColor # color off (usu. Colors.Normal)
51 51 ))
52 52
53 53 # make some schemes as instances so we can copy them for modification easily:
54 54 __PColLinux = coloransi.ColorScheme(
55 55 'Linux',
56 56 in_prompt = InputColors.Green,
57 57 in_number = InputColors.LightGreen,
58 58 in_prompt2 = InputColors.Green,
59 59 in_normal = InputColors.Normal, # color off (usu. Colors.Normal)
60 60
61 61 out_prompt = Colors.Red,
62 62 out_number = Colors.LightRed,
63 63
64 64 normal = Colors.Normal
65 65 )
66 66 # Don't forget to enter it into the table!
67 67 PromptColors.add_scheme(__PColLinux)
68 68
69 69 # Slightly modified Linux for light backgrounds
70 70 __PColLightBG = __PColLinux.copy('LightBG')
71 71
72 72 __PColLightBG.colors.update(
73 73 in_prompt = InputColors.Blue,
74 74 in_number = InputColors.LightBlue,
75 75 in_prompt2 = InputColors.Blue
76 76 )
77 77 PromptColors.add_scheme(__PColLightBG)
78 78
79 79 del Colors,InputColors
80 80
81 81 #-----------------------------------------------------------------------------
82 82 def multiple_replace(dict, text):
83 83 """ Replace in 'text' all occurences of any key in the given
84 84 dictionary by its corresponding value. Returns the new string."""
85 85
86 86 # Function by Xavier Defrang, originally found at:
87 87 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81330
88 88
89 89 # Create a regular expression from the dictionary keys
90 90 regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))
91 91 # For each match, look-up corresponding value in dictionary
92 92 return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
93 93
94 94 #-----------------------------------------------------------------------------
95 95 # Special characters that can be used in prompt templates, mainly bash-like
96 96
97 97 # If $HOME isn't defined (Windows), make it an absurd string so that it can
98 98 # never be expanded out into '~'. Basically anything which can never be a
99 99 # reasonable directory name will do, we just want the $HOME -> '~' operation
100 100 # to become a no-op. We pre-compute $HOME here so it's not done on every
101 101 # prompt call.
102 102
103 103 # FIXME:
104 104
105 105 # - This should be turned into a class which does proper namespace management,
106 106 # since the prompt specials need to be evaluated in a certain namespace.
107 107 # Currently it's just globals, which need to be managed manually by code
108 108 # below.
109 109
110 110 # - I also need to split up the color schemes from the prompt specials
111 111 # somehow. I don't have a clean design for that quite yet.
112 112
113 113 HOME = os.environ.get("HOME","//////:::::ZZZZZ,,,~~~")
114 114
115 115 # We precompute a few more strings here for the prompt_specials, which are
116 116 # fixed once ipython starts. This reduces the runtime overhead of computing
117 117 # prompt strings.
118 118 USER = os.environ.get("USER")
119 119 HOSTNAME = socket.gethostname()
120 120 HOSTNAME_SHORT = HOSTNAME.split(".")[0]
121 121 ROOT_SYMBOL = "$#"[os.name=='nt' or os.getuid()==0]
122 122
123 123 prompt_specials_color = {
124 124 # Prompt/history count
125 125 '%n' : '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
126 126 r'\#': '${self.col_num}' '${self.cache.prompt_count}' '${self.col_p}',
127 127 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
128 128 # can get numbers displayed in whatever color they want.
129 129 r'\N': '${self.cache.prompt_count}',
130 130
131 131 # Prompt/history count, with the actual digits replaced by dots. Used
132 132 # mainly in continuation prompts (prompt_in2)
133 133 #r'\D': '${"."*len(str(self.cache.prompt_count))}',
134 134 # More robust form of the above expression, that uses __builtins__
135 135 r'\D': '${"."*__builtins__.len(__builtins__.str(self.cache.prompt_count))}',
136 136
137 137 # Current working directory
138 138 r'\w': '${os.getcwd()}',
139 139 # Current time
140 140 r'\t' : '${time.strftime("%H:%M:%S")}',
141 141 # Basename of current working directory.
142 142 # (use os.sep to make this portable across OSes)
143 143 r'\W' : '${os.getcwd().split("%s")[-1]}' % os.sep,
144 144 # These X<N> are an extension to the normal bash prompts. They return
145 145 # N terms of the path, after replacing $HOME with '~'
146 146 r'\X0': '${os.getcwd().replace("%s","~")}' % HOME,
147 147 r'\X1': '${self.cwd_filt(1)}',
148 148 r'\X2': '${self.cwd_filt(2)}',
149 149 r'\X3': '${self.cwd_filt(3)}',
150 150 r'\X4': '${self.cwd_filt(4)}',
151 151 r'\X5': '${self.cwd_filt(5)}',
152 152 # Y<N> are similar to X<N>, but they show '~' if it's the directory
153 153 # N+1 in the list. Somewhat like %cN in tcsh.
154 154 r'\Y0': '${self.cwd_filt2(0)}',
155 155 r'\Y1': '${self.cwd_filt2(1)}',
156 156 r'\Y2': '${self.cwd_filt2(2)}',
157 157 r'\Y3': '${self.cwd_filt2(3)}',
158 158 r'\Y4': '${self.cwd_filt2(4)}',
159 159 r'\Y5': '${self.cwd_filt2(5)}',
160 160 # Hostname up to first .
161 161 r'\h': HOSTNAME_SHORT,
162 162 # Full hostname
163 163 r'\H': HOSTNAME,
164 164 # Username of current user
165 165 r'\u': USER,
166 166 # Escaped '\'
167 167 '\\\\': '\\',
168 168 # Newline
169 169 r'\n': '\n',
170 170 # Carriage return
171 171 r'\r': '\r',
172 172 # Release version
173 173 r'\v': Release.version,
174 174 # Root symbol ($ or #)
175 175 r'\$': ROOT_SYMBOL,
176 176 }
177 177
178 178 # A copy of the prompt_specials dictionary but with all color escapes removed,
179 179 # so we can correctly compute the prompt length for the auto_rewrite method.
180 180 prompt_specials_nocolor = prompt_specials_color.copy()
181 181 prompt_specials_nocolor['%n'] = '${self.cache.prompt_count}'
182 182 prompt_specials_nocolor[r'\#'] = '${self.cache.prompt_count}'
183 183
184 184 # Add in all the InputTermColors color escapes as valid prompt characters.
185 185 # They all get added as \\C_COLORNAME, so that we don't have any conflicts
186 186 # with a color name which may begin with a letter used by any other of the
187 187 # allowed specials. This of course means that \\C will never be allowed for
188 188 # anything else.
189 189 input_colors = coloransi.InputTermColors
190 190 for _color in dir(input_colors):
191 191 if _color[0] != '_':
192 192 c_name = r'\C_'+_color
193 193 prompt_specials_color[c_name] = getattr(input_colors,_color)
194 194 prompt_specials_nocolor[c_name] = ''
195 195
196 196 # we default to no color for safety. Note that prompt_specials is a global
197 197 # variable used by all prompt objects.
198 198 prompt_specials = prompt_specials_nocolor
199 199
200 200 #-----------------------------------------------------------------------------
201 201 def str_safe(arg):
202 202 """Convert to a string, without ever raising an exception.
203 203
204 204 If str(arg) fails, <ERROR: ... > is returned, where ... is the exception
205 205 error message."""
206 206
207 207 try:
208 208 out = str(arg)
209 209 except UnicodeError:
210 210 try:
211 211 out = arg.encode('utf_8','replace')
212 212 except Exception,msg:
213 213 # let's keep this little duplication here, so that the most common
214 214 # case doesn't suffer from a double try wrapping.
215 215 out = '<ERROR: %s>' % msg
216 216 except Exception,msg:
217 217 out = '<ERROR: %s>' % msg
218 218 return out
219 219
220 220 class BasePrompt(object):
221 221 """Interactive prompt similar to Mathematica's."""
222 222
223 223 def _get_p_template(self):
224 224 return self._p_template
225 225
226 226 def _set_p_template(self,val):
227 227 self._p_template = val
228 228 self.set_p_str()
229 229
230 230 p_template = property(_get_p_template,_set_p_template,
231 231 doc='Template for prompt string creation')
232 232
233 233 def __init__(self,cache,sep,prompt,pad_left=False):
234 234
235 235 # Hack: we access information about the primary prompt through the
236 236 # cache argument. We need this, because we want the secondary prompt
237 237 # to be aligned with the primary one. Color table info is also shared
238 238 # by all prompt classes through the cache. Nice OO spaghetti code!
239 239 self.cache = cache
240 240 self.sep = sep
241 241
242 242 # regexp to count the number of spaces at the end of a prompt
243 243 # expression, useful for prompt auto-rewriting
244 244 self.rspace = re.compile(r'(\s*)$')
245 245 # Flag to left-pad prompt strings to match the length of the primary
246 246 # prompt
247 247 self.pad_left = pad_left
248 248
249 249 # Set template to create each actual prompt (where numbers change).
250 250 # Use a property
251 251 self.p_template = prompt
252 252 self.set_p_str()
253 253
254 254 def set_p_str(self):
255 255 """ Set the interpolating prompt strings.
256 256
257 257 This must be called every time the color settings change, because the
258 258 prompt_specials global may have changed."""
259 259
260 260 import os,time # needed in locals for prompt string handling
261 261 loc = locals()
262 262 try:
263 263 self.p_str = ItplNS('%s%s%s' %
264 264 ('${self.sep}${self.col_p}',
265 265 multiple_replace(prompt_specials, self.p_template),
266 266 '${self.col_norm}'),self.cache.user_ns,loc)
267 267
268 268 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
269 269 self.p_template),
270 270 self.cache.user_ns,loc)
271 271 except:
272 272 print "Illegal prompt template (check $ usage!):",self.p_template
273 273 self.p_str = self.p_template
274 274 self.p_str_nocolor = self.p_template
275 275
276 276 def write(self,msg): # dbg
277 277 sys.stdout.write(msg)
278 278 return ''
279 279
280 280 def __str__(self):
281 281 """Return a string form of the prompt.
282 282
283 283 This for is useful for continuation and output prompts, since it is
284 284 left-padded to match lengths with the primary one (if the
285 285 self.pad_left attribute is set)."""
286 286
287 287 out_str = str_safe(self.p_str)
288 288 if self.pad_left:
289 289 # We must find the amount of padding required to match lengths,
290 290 # taking the color escapes (which are invisible on-screen) into
291 291 # account.
292 292 esc_pad = len(out_str) - len(str_safe(self.p_str_nocolor))
293 293 format = '%%%ss' % (len(str(self.cache.last_prompt))+esc_pad)
294 294 return format % out_str
295 295 else:
296 296 return out_str
297 297
298 298 # these path filters are put in as methods so that we can control the
299 299 # namespace where the prompt strings get evaluated
300 300 def cwd_filt(self,depth):
301 301 """Return the last depth elements of the current working directory.
302 302
303 303 $HOME is always replaced with '~'.
304 304 If depth==0, the full path is returned."""
305 305
306 306 cwd = os.getcwd().replace(HOME,"~")
307 307 out = os.sep.join(cwd.split(os.sep)[-depth:])
308 308 if out:
309 309 return out
310 310 else:
311 311 return os.sep
312 312
313 313 def cwd_filt2(self,depth):
314 314 """Return the last depth elements of the current working directory.
315 315
316 316 $HOME is always replaced with '~'.
317 317 If depth==0, the full path is returned."""
318 318
319 319 full_cwd = os.getcwd()
320 320 cwd = full_cwd.replace(HOME,"~").split(os.sep)
321 321 if '~' in cwd and len(cwd) == depth+1:
322 322 depth += 1
323 323 drivepart = ''
324 324 if sys.platform == 'win32' and len(cwd) > depth:
325 325 drivepart = os.path.splitdrive(full_cwd)[0]
326 326 out = drivepart + '/'.join(cwd[-depth:])
327 327
328 328 if out:
329 329 return out
330 330 else:
331 331 return os.sep
332 332
333 333 def __nonzero__(self):
334 334 """Implement boolean behavior.
335 335
336 336 Checks whether the p_str attribute is non-empty"""
337 337
338 338 return bool(self.p_template)
339 339
340 340 class Prompt1(BasePrompt):
341 341 """Input interactive prompt similar to Mathematica's."""
342 342
343 343 def __init__(self,cache,sep='\n',prompt='In [\\#]: ',pad_left=True):
344 344 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
345 345
346 346 def set_colors(self):
347 347 self.set_p_str()
348 348 Colors = self.cache.color_table.active_colors # shorthand
349 349 self.col_p = Colors.in_prompt
350 350 self.col_num = Colors.in_number
351 351 self.col_norm = Colors.in_normal
352 352 # We need a non-input version of these escapes for the '--->'
353 353 # auto-call prompts used in the auto_rewrite() method.
354 354 self.col_p_ni = self.col_p.replace('\001','').replace('\002','')
355 355 self.col_norm_ni = Colors.normal
356 356
357 357 def __str__(self):
358 358 self.cache.prompt_count += 1
359 359 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1]
360 360 return str_safe(self.p_str)
361 361
362 362 def auto_rewrite(self):
363 363 """Print a string of the form '--->' which lines up with the previous
364 364 input string. Useful for systems which re-write the user input when
365 365 handling automatically special syntaxes."""
366 366
367 367 curr = str(self.cache.last_prompt)
368 368 nrspaces = len(self.rspace.search(curr).group())
369 369 return '%s%s>%s%s' % (self.col_p_ni,'-'*(len(curr)-nrspaces-1),
370 370 ' '*nrspaces,self.col_norm_ni)
371 371
372 372 class PromptOut(BasePrompt):
373 373 """Output interactive prompt similar to Mathematica's."""
374 374
375 375 def __init__(self,cache,sep='',prompt='Out[\\#]: ',pad_left=True):
376 376 BasePrompt.__init__(self,cache,sep,prompt,pad_left)
377 377 if not self.p_template:
378 378 self.__str__ = lambda: ''
379 379
380 380 def set_colors(self):
381 381 self.set_p_str()
382 382 Colors = self.cache.color_table.active_colors # shorthand
383 383 self.col_p = Colors.out_prompt
384 384 self.col_num = Colors.out_number
385 385 self.col_norm = Colors.normal
386 386
387 387 class Prompt2(BasePrompt):
388 388 """Interactive continuation prompt."""
389 389
390 390 def __init__(self,cache,prompt=' .\\D.: ',pad_left=True):
391 391 self.cache = cache
392 392 self.p_template = prompt
393 393 self.pad_left = pad_left
394 394 self.set_p_str()
395 395
396 396 def set_p_str(self):
397 397 import os,time # needed in locals for prompt string handling
398 398 loc = locals()
399 399 self.p_str = ItplNS('%s%s%s' %
400 400 ('${self.col_p2}',
401 401 multiple_replace(prompt_specials, self.p_template),
402 402 '$self.col_norm'),
403 403 self.cache.user_ns,loc)
404 404 self.p_str_nocolor = ItplNS(multiple_replace(prompt_specials_nocolor,
405 405 self.p_template),
406 406 self.cache.user_ns,loc)
407 407
408 408 def set_colors(self):
409 409 self.set_p_str()
410 410 Colors = self.cache.color_table.active_colors
411 411 self.col_p2 = Colors.in_prompt2
412 412 self.col_norm = Colors.in_normal
413 413 # FIXME (2004-06-16) HACK: prevent crashes for users who haven't
414 414 # updated their prompt_in2 definitions. Remove eventually.
415 415 self.col_p = Colors.out_prompt
416 416 self.col_num = Colors.out_number
417 417
418 418
419 419 #-----------------------------------------------------------------------------
420 420 class CachedOutput:
421 421 """Class for printing output from calculations while keeping a cache of
422 422 reults. It dynamically creates global variables prefixed with _ which
423 423 contain these results.
424 424
425 425 Meant to be used as a sys.displayhook replacement, providing numbered
426 426 prompts and cache services.
427 427
428 428 Initialize with initial and final values for cache counter (this defines
429 429 the maximum size of the cache."""
430 430
431 431 def __init__(self,shell,cache_size,Pprint,
432 432 colors='NoColor',input_sep='\n',
433 433 output_sep='\n',output_sep2='',
434 434 ps1 = None, ps2 = None,ps_out = None,pad_left=True):
435 435
436 436 cache_size_min = 3
437 437 if cache_size <= 0:
438 438 self.do_full_cache = 0
439 439 cache_size = 0
440 440 elif cache_size < cache_size_min:
441 441 self.do_full_cache = 0
442 442 cache_size = 0
443 443 warn('caching was disabled (min value for cache size is %s).' %
444 444 cache_size_min,level=3)
445 445 else:
446 446 self.do_full_cache = 1
447 447
448 448 self.cache_size = cache_size
449 449 self.input_sep = input_sep
450 450
451 451 # we need a reference to the user-level namespace
452 452 self.shell = shell
453 453 self.user_ns = shell.user_ns
454 454 # and to the user's input
455 455 self.input_hist = shell.input_hist
456 456 # and to the user's logger, for logging output
457 457 self.logger = shell.logger
458 458
459 459 # Set input prompt strings and colors
460 460 if cache_size == 0:
461 461 if ps1.find('%n') > -1 or ps1.find(r'\#') > -1 \
462 462 or ps1.find(r'\N') > -1:
463 463 ps1 = '>>> '
464 464 if ps2.find('%n') > -1 or ps2.find(r'\#') > -1 \
465 465 or ps2.find(r'\N') > -1:
466 466 ps2 = '... '
467 467 self.ps1_str = self._set_prompt_str(ps1,'In [\\#]: ','>>> ')
468 468 self.ps2_str = self._set_prompt_str(ps2,' .\\D.: ','... ')
469 469 self.ps_out_str = self._set_prompt_str(ps_out,'Out[\\#]: ','')
470 470
471 471 self.color_table = PromptColors
472 472 self.prompt1 = Prompt1(self,sep=input_sep,prompt=self.ps1_str,
473 473 pad_left=pad_left)
474 474 self.prompt2 = Prompt2(self,prompt=self.ps2_str,pad_left=pad_left)
475 475 self.prompt_out = PromptOut(self,sep='',prompt=self.ps_out_str,
476 476 pad_left=pad_left)
477 477 self.set_colors(colors)
478 478
479 479 # other more normal stuff
480 480 # b/c each call to the In[] prompt raises it by 1, even the first.
481 481 self.prompt_count = 0
482 482 # Store the last prompt string each time, we need it for aligning
483 483 # continuation and auto-rewrite prompts
484 484 self.last_prompt = ''
485 485 self.Pprint = Pprint
486 486 self.output_sep = output_sep
487 487 self.output_sep2 = output_sep2
488 488 self._,self.__,self.___ = '','',''
489 489 self.pprint_types = map(type,[(),[],{}])
490 490
491 491 # these are deliberately global:
492 492 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
493 493 self.user_ns.update(to_user_ns)
494 494
495 495 def _set_prompt_str(self,p_str,cache_def,no_cache_def):
496 496 if p_str is None:
497 497 if self.do_full_cache:
498 498 return cache_def
499 499 else:
500 500 return no_cache_def
501 501 else:
502 502 return p_str
503 503
504 504 def set_colors(self,colors):
505 505 """Set the active color scheme and configure colors for the three
506 506 prompt subsystems."""
507 507
508 508 # FIXME: the prompt_specials global should be gobbled inside this
509 509 # class instead. Do it when cleaning up the whole 3-prompt system.
510 510 global prompt_specials
511 511 if colors.lower()=='nocolor':
512 512 prompt_specials = prompt_specials_nocolor
513 513 else:
514 514 prompt_specials = prompt_specials_color
515 515
516 516 self.color_table.set_active_scheme(colors)
517 517 self.prompt1.set_colors()
518 518 self.prompt2.set_colors()
519 519 self.prompt_out.set_colors()
520 520
521 521 def __call__(self,arg=None):
522 522 """Printing with history cache management.
523 523
524 524 This is invoked everytime the interpreter needs to print, and is
525 525 activated by setting the variable sys.displayhook to it."""
526 526
527 527 # If something injected a '_' variable in __builtin__, delete
528 528 # ipython's automatic one so we don't clobber that. gettext() in
529 529 # particular uses _, so we need to stay away from it.
530 530 if '_' in __builtin__.__dict__:
531 531 try:
532 532 del self.user_ns['_']
533 533 except KeyError:
534 534 pass
535 535 if arg is not None:
536 536 cout_write = Term.cout.write # fast lookup
537 537 # first handle the cache and counters
538 538
539 539 # do not print output if input ends in ';'
540 540 try:
541 541 if self.input_hist[self.prompt_count].endswith(';\n'):
542 542 return
543 543 except IndexError:
544 544 # some uses of ipshellembed may fail here
545 545 pass
546 546 # don't use print, puts an extra space
547 547 cout_write(self.output_sep)
548 548 outprompt = self.shell.hooks.generate_output_prompt()
549 549 if self.do_full_cache:
550 550 cout_write(outprompt)
551 551
552 552 # and now call a possibly user-defined print mechanism
553 553 manipulated_val = self.display(arg)
554 554
555 555 # user display hooks can change the variable to be stored in
556 556 # output history
557 557
558 558 if manipulated_val is not None:
559 559 arg = manipulated_val
560 560
561 561 # avoid recursive reference when displaying _oh/Out
562 562 if arg is not self.user_ns['_oh']:
563 563 self.update(arg)
564 564
565 565 if self.logger.log_output:
566 566 self.logger.log_write(repr(arg),'output')
567 567 cout_write(self.output_sep2)
568 568 Term.cout.flush()
569 569
570 570 def _display(self,arg):
571 571 """Default printer method, uses pprint.
572 572
573 573 Do ip.set_hook("result_display", my_displayhook) for custom result
574 574 display, e.g. when your own objects need special formatting.
575 575 """
576 576 try:
577 577 return IPython.utils.generics.result_display(arg)
578 578 except TryNext:
579 579 return self.shell.hooks.result_display(arg)
580 580
581 581 # Assign the default display method:
582 582 display = _display
583 583
584 584 def update(self,arg):
585 585 #print '***cache_count', self.cache_count # dbg
586 586 if len(self.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
587 587 warn('Output cache limit (currently '+
588 588 `self.cache_size`+' entries) hit.\n'
589 589 'Flushing cache and resetting history counter...\n'
590 590 'The only history variables available will be _,__,___ and _1\n'
591 591 'with the current result.')
592 592
593 593 self.flush()
594 594 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
595 595 # we cause buggy behavior for things like gettext).
596 596 if '_' not in __builtin__.__dict__:
597 597 self.___ = self.__
598 598 self.__ = self._
599 599 self._ = arg
600 600 self.user_ns.update({'_':self._,'__':self.__,'___':self.___})
601 601
602 602 # hackish access to top-level namespace to create _1,_2... dynamically
603 603 to_main = {}
604 604 if self.do_full_cache:
605 605 new_result = '_'+`self.prompt_count`
606 606 to_main[new_result] = arg
607 607 self.user_ns.update(to_main)
608 608 self.user_ns['_oh'][self.prompt_count] = arg
609 609
610 610 def flush(self):
611 611 if not self.do_full_cache:
612 612 raise ValueError,"You shouldn't have reached the cache flush "\
613 613 "if full caching is not enabled!"
614 614 # delete auto-generated vars from global namespace
615 615
616 616 for n in range(1,self.prompt_count + 1):
617 617 key = '_'+`n`
618 618 try:
619 619 del self.user_ns[key]
620 620 except: pass
621 621 self.user_ns['_oh'].clear()
622 622
623 623 if '_' not in __builtin__.__dict__:
624 624 self.user_ns.update({'_':None,'__':None, '___':None})
625 625 import gc
626 626 gc.collect() # xxx needed?
627 627
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from IPython/ipapi.py to IPython/core/ipapi.py
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now