##// END OF EJS Templates
check for set (python 2.3 compatibility). Closes #209
vivainio -
Show More
@@ -1,1121 +1,1125 b''
1 # -*- coding: iso-8859-1 -*-
1 # -*- coding: iso-8859-1 -*-
2
2
3 import ipipe, os, webbrowser, urllib
3 import ipipe, os, webbrowser, urllib
4 from IPython import ipapi
4 from IPython import ipapi
5 import wx
5 import wx
6 import wx.grid, wx.html
6 import wx.grid, wx.html
7
7
8 try:
8 try:
9 sorted
9 sorted
10 except NameError:
10 except NameError:
11 from ipipe import sorted
11 from ipipe import sorted
12 try:
13 set
14 except:
15 from sets import Set as set
12
16
13
17
14 __all__ = ["igrid"]
18 __all__ = ["igrid"]
15
19
16
20
17 help = """
21 help = """
18 <?xml version='1.0' encoding='iso-8859-1'?>
22 <?xml version='1.0' encoding='iso-8859-1'?>
19 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
23 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
20 <html>
24 <html>
21 <head>
25 <head>
22 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
26 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
23 <link rel="stylesheet" href="igrid_help.css" type="text/css" />
27 <link rel="stylesheet" href="igrid_help.css" type="text/css" />
24 <title>igrid help</title>
28 <title>igrid help</title>
25 </head>
29 </head>
26 <body>
30 <body>
27 <h1>igrid help</h1>
31 <h1>igrid help</h1>
28
32
29
33
30 <h2>Commands</h2>
34 <h2>Commands</h2>
31
35
32
36
33 <h3>pick (P)</h3>
37 <h3>pick (P)</h3>
34 <p>Pick the whole row (object is available as "_")</p>
38 <p>Pick the whole row (object is available as "_")</p>
35
39
36 <h3>pickattr (Shift-P)</h3>
40 <h3>pickattr (Shift-P)</h3>
37 <p>Pick the attribute under the cursor</p>
41 <p>Pick the attribute under the cursor</p>
38
42
39 <h3>pickallattrs (Shift-C)</h3>
43 <h3>pickallattrs (Shift-C)</h3>
40 <p>Pick the complete column under the cursor (i.e. the attribute under the
44 <p>Pick the complete column under the cursor (i.e. the attribute under the
41 cursor) from all currently fetched objects. These attributes will be returned
45 cursor) from all currently fetched objects. These attributes will be returned
42 as a list.</p>
46 as a list.</p>
43
47
44 <h3>pickinput (I)</h3>
48 <h3>pickinput (I)</h3>
45 <p>Pick the current row as next input line in IPython. Additionally the row is stored as "_"</p>
49 <p>Pick the current row as next input line in IPython. Additionally the row is stored as "_"</p>
46
50
47 <h3>pickinputattr (Shift-I)</h3>
51 <h3>pickinputattr (Shift-I)</h3>
48 <p>Pick the attribute under the cursor as next input line in IPython. Additionally the row is stored as "_"</p>
52 <p>Pick the attribute under the cursor as next input line in IPython. Additionally the row is stored as "_"</p>
49
53
50 <h3>enter (E)</h3>
54 <h3>enter (E)</h3>
51 <p>Enter the object under the cursor. (what this mean depends on the object
55 <p>Enter the object under the cursor. (what this mean depends on the object
52 itself, i.e. how it implements iteration). This opens a new browser 'level'.</p>
56 itself, i.e. how it implements iteration). This opens a new browser 'level'.</p>
53
57
54 <h3>enterattr (Shift-E)</h3>
58 <h3>enterattr (Shift-E)</h3>
55 <p>Enter the attribute under the cursor.</p>
59 <p>Enter the attribute under the cursor.</p>
56
60
57 <h3>detail (D)</h3>
61 <h3>detail (D)</h3>
58 <p>Show a detail view of the object under the cursor. This shows the name,
62 <p>Show a detail view of the object under the cursor. This shows the name,
59 type, doc string and value of the object attributes (and it might show more
63 type, doc string and value of the object attributes (and it might show more
60 attributes than in the list view, depending on the object).</p>
64 attributes than in the list view, depending on the object).</p>
61
65
62 <h3>detailattr (Shift-D)</h3>
66 <h3>detailattr (Shift-D)</h3>
63 <p>Show a detail view of the attribute under the cursor.</p>
67 <p>Show a detail view of the attribute under the cursor.</p>
64
68
65 <h3>pickrows (M)</h3>
69 <h3>pickrows (M)</h3>
66 <p>Pick multiple selected rows (M)</p>
70 <p>Pick multiple selected rows (M)</p>
67
71
68 <h3>pickrowsattr (CTRL-M)</h3>
72 <h3>pickrowsattr (CTRL-M)</h3>
69 <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p>
73 <p>From multiple selected rows pick the cells matching the attribute the cursor is in (CTRL-M)</p>
70
74
71 <h3>find (CTRL-F)</h3>
75 <h3>find (CTRL-F)</h3>
72 <p>Find text</p>
76 <p>Find text</p>
73
77
74 <h3>find_expression (CTRL-Shift-F)</h3>
78 <h3>find_expression (CTRL-Shift-F)</h3>
75 <p>Find entries matching an expression</p>
79 <p>Find entries matching an expression</p>
76
80
77 <h3>find_next (F3)</h3>
81 <h3>find_next (F3)</h3>
78 <p>Find next occurrence</p>
82 <p>Find next occurrence</p>
79
83
80 <h3>find_previous (Shift-F3)</h3>
84 <h3>find_previous (Shift-F3)</h3>
81 <p>Find previous occurrence</p>
85 <p>Find previous occurrence</p>
82
86
83 <h3>sortattrasc (V)</h3>
87 <h3>sortattrasc (V)</h3>
84 <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p>
88 <p>Sort the objects (in ascending order) using the attribute under the cursor as the sort key.</p>
85
89
86 <h3>sortattrdesc (Shift-V)</h3>
90 <h3>sortattrdesc (Shift-V)</h3>
87 <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p>
91 <p>Sort the objects (in descending order) using the attribute under the cursor as the sort key.</p>
88
92
89 <h3>refresh_once (R, F5)</h3>
93 <h3>refresh_once (R, F5)</h3>
90 <p>Refreshes the display by restarting the iterator</p>
94 <p>Refreshes the display by restarting the iterator</p>
91
95
92 <h3>refresh_every_second</h3>
96 <h3>refresh_every_second</h3>
93 <p>Refreshes the display by restarting the iterator every second until stopped by stop_refresh.</p>
97 <p>Refreshes the display by restarting the iterator every second until stopped by stop_refresh.</p>
94
98
95 <h3>refresh_interval</h3>
99 <h3>refresh_interval</h3>
96 <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>
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>
97
101
98 <h3>stop_refresh</h3>
102 <h3>stop_refresh</h3>
99 <p>Stops all refresh timers.</p>
103 <p>Stops all refresh timers.</p>
100
104
101 <h3>leave (Backspace, DEL, X)</h3>
105 <h3>leave (Backspace, DEL, X)</h3>
102 <p>Close current tab (and all the tabs to the right of the current one).</h3>
106 <p>Close current tab (and all the tabs to the right of the current one).</h3>
103
107
104 <h3>quit (ESC,Q)</h3>
108 <h3>quit (ESC,Q)</h3>
105 <p>Quit igrid and return to the IPython prompt.</p>
109 <p>Quit igrid and return to the IPython prompt.</p>
106
110
107
111
108 <h2>Navigation</h2>
112 <h2>Navigation</h2>
109
113
110
114
111 <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3>
115 <h3>Jump to the last column of the current row (END, CTRL-E, CTRL-Right)</h3>
112
116
113 <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3>
117 <h3>Jump to the first column of the current row (HOME, CTRL-A, CTRL-Left)</h3>
114
118
115 <h3>Move the cursor one column to the left (&lt;)</h3>
119 <h3>Move the cursor one column to the left (&lt;)</h3>
116
120
117 <h3>Move the cursor one column to the right (&gt;)</h3>
121 <h3>Move the cursor one column to the right (&gt;)</h3>
118
122
119 <h3>Jump to the first row in the current column (CTRL-Up)</h3>
123 <h3>Jump to the first row in the current column (CTRL-Up)</h3>
120
124
121 <h3>Jump to the last row in the current column (CTRL-Down)</h3>
125 <h3>Jump to the last row in the current column (CTRL-Down)</h3>
122
126
123 </body>
127 </body>
124 </html>
128 </html>
125
129
126 """
130 """
127
131
128
132
129 class IGridRenderer(wx.grid.PyGridCellRenderer):
133 class IGridRenderer(wx.grid.PyGridCellRenderer):
130 """
134 """
131 This is a custom renderer for our IGridGrid
135 This is a custom renderer for our IGridGrid
132 """
136 """
133 def __init__(self, table):
137 def __init__(self, table):
134 self.maxchars = 200
138 self.maxchars = 200
135 self.table = table
139 self.table = table
136 self.colormap = (
140 self.colormap = (
137 ( 0, 0, 0),
141 ( 0, 0, 0),
138 (174, 0, 0),
142 (174, 0, 0),
139 ( 0, 174, 0),
143 ( 0, 174, 0),
140 (174, 174, 0),
144 (174, 174, 0),
141 ( 0, 0, 174),
145 ( 0, 0, 174),
142 (174, 0, 174),
146 (174, 0, 174),
143 ( 0, 174, 174),
147 ( 0, 174, 174),
144 ( 64, 64, 64)
148 ( 64, 64, 64)
145 )
149 )
146
150
147 wx.grid.PyGridCellRenderer.__init__(self)
151 wx.grid.PyGridCellRenderer.__init__(self)
148
152
149 def _getvalue(self, row, col):
153 def _getvalue(self, row, col):
150 try:
154 try:
151 value = self.table._displayattrs[col].value(self.table.items[row])
155 value = self.table._displayattrs[col].value(self.table.items[row])
152 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
156 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
153 except Exception, exc:
157 except Exception, exc:
154 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
158 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
155 return (align, text)
159 return (align, text)
156
160
157 def GetBestSize(self, grid, attr, dc, row, col):
161 def GetBestSize(self, grid, attr, dc, row, col):
158 text = grid.GetCellValue(row, col)
162 text = grid.GetCellValue(row, col)
159 (align, text) = self._getvalue(row, col)
163 (align, text) = self._getvalue(row, col)
160 dc.SetFont(attr.GetFont())
164 dc.SetFont(attr.GetFont())
161 (w, h) = dc.GetTextExtent(str(text))
165 (w, h) = dc.GetTextExtent(str(text))
162 return wx.Size(min(w+2, 600), h+2) # add border
166 return wx.Size(min(w+2, 600), h+2) # add border
163
167
164 def Draw(self, grid, attr, dc, rect, row, col, isSelected):
168 def Draw(self, grid, attr, dc, rect, row, col, isSelected):
165 """
169 """
166 Takes care of drawing everything in the cell; aligns the text
170 Takes care of drawing everything in the cell; aligns the text
167 """
171 """
168 text = grid.GetCellValue(row, col)
172 text = grid.GetCellValue(row, col)
169 (align, text) = self._getvalue(row, col)
173 (align, text) = self._getvalue(row, col)
170 if isSelected:
174 if isSelected:
171 bg = grid.GetSelectionBackground()
175 bg = grid.GetSelectionBackground()
172 else:
176 else:
173 bg = ["white", (240, 240, 240)][row%2]
177 bg = ["white", (240, 240, 240)][row%2]
174 dc.SetTextBackground(bg)
178 dc.SetTextBackground(bg)
175 dc.SetBrush(wx.Brush(bg, wx.SOLID))
179 dc.SetBrush(wx.Brush(bg, wx.SOLID))
176 dc.SetPen(wx.TRANSPARENT_PEN)
180 dc.SetPen(wx.TRANSPARENT_PEN)
177 dc.SetFont(attr.GetFont())
181 dc.SetFont(attr.GetFont())
178 dc.DrawRectangleRect(rect)
182 dc.DrawRectangleRect(rect)
179 dc.SetClippingRect(rect)
183 dc.SetClippingRect(rect)
180 # Format the text
184 # Format the text
181 if align == -1: # left alignment
185 if align == -1: # left alignment
182 (width, height) = dc.GetTextExtent(str(text))
186 (width, height) = dc.GetTextExtent(str(text))
183 x = rect[0]+1
187 x = rect[0]+1
184 y = rect[1]+0.5*(rect[3]-height)
188 y = rect[1]+0.5*(rect[3]-height)
185
189
186 for (style, part) in text:
190 for (style, part) in text:
187 if isSelected:
191 if isSelected:
188 fg = grid.GetSelectionForeground()
192 fg = grid.GetSelectionForeground()
189 else:
193 else:
190 fg = self.colormap[style.fg]
194 fg = self.colormap[style.fg]
191 dc.SetTextForeground(fg)
195 dc.SetTextForeground(fg)
192 (w, h) = dc.GetTextExtent(part)
196 (w, h) = dc.GetTextExtent(part)
193 dc.DrawText(part, x, y)
197 dc.DrawText(part, x, y)
194 x += w
198 x += w
195 elif align == 0: # center alignment
199 elif align == 0: # center alignment
196 (width, height) = dc.GetTextExtent(str(text))
200 (width, height) = dc.GetTextExtent(str(text))
197 x = rect[0]+0.5*(rect[2]-width)
201 x = rect[0]+0.5*(rect[2]-width)
198 y = rect[1]+0.5*(rect[3]-height)
202 y = rect[1]+0.5*(rect[3]-height)
199 for (style, part) in text:
203 for (style, part) in text:
200 if isSelected:
204 if isSelected:
201 fg = grid.GetSelectionForeground()
205 fg = grid.GetSelectionForeground()
202 else:
206 else:
203 fg = self.colormap[style.fg]
207 fg = self.colormap[style.fg]
204 dc.SetTextForeground(fg)
208 dc.SetTextForeground(fg)
205 (w, h) = dc.GetTextExtent(part)
209 (w, h) = dc.GetTextExtent(part)
206 dc.DrawText(part, x, y)
210 dc.DrawText(part, x, y)
207 x += w
211 x += w
208 else: # right alignment
212 else: # right alignment
209 (width, height) = dc.GetTextExtent(str(text))
213 (width, height) = dc.GetTextExtent(str(text))
210 x = rect[0]+rect[2]-1
214 x = rect[0]+rect[2]-1
211 y = rect[1]+0.5*(rect[3]-height)
215 y = rect[1]+0.5*(rect[3]-height)
212 for (style, part) in reversed(text):
216 for (style, part) in reversed(text):
213 (w, h) = dc.GetTextExtent(part)
217 (w, h) = dc.GetTextExtent(part)
214 x -= w
218 x -= w
215 if isSelected:
219 if isSelected:
216 fg = grid.GetSelectionForeground()
220 fg = grid.GetSelectionForeground()
217 else:
221 else:
218 fg = self.colormap[style.fg]
222 fg = self.colormap[style.fg]
219 dc.SetTextForeground(fg)
223 dc.SetTextForeground(fg)
220 dc.DrawText(part, x, y)
224 dc.DrawText(part, x, y)
221 dc.DestroyClippingRegion()
225 dc.DestroyClippingRegion()
222
226
223 def Clone(self):
227 def Clone(self):
224 return IGridRenderer(self.table)
228 return IGridRenderer(self.table)
225
229
226
230
227 class IGridTable(wx.grid.PyGridTableBase):
231 class IGridTable(wx.grid.PyGridTableBase):
228 # The data table for the ``IGridGrid``. Some dirty tricks were used here:
232 # The data table for the ``IGridGrid``. Some dirty tricks were used here:
229 # ``GetValue()`` does not get any values (or at least it does not return
233 # ``GetValue()`` does not get any values (or at least it does not return
230 # anything, accessing the values is done by the renderer)
234 # anything, accessing the values is done by the renderer)
231 # but rather tries to fetch the objects which were requested into the table.
235 # but rather tries to fetch the objects which were requested into the table.
232 # General behaviour is: Fetch the first X objects. If the user scrolls down
236 # General behaviour is: Fetch the first X objects. If the user scrolls down
233 # to the last object another bunch of X objects is fetched (if possible)
237 # to the last object another bunch of X objects is fetched (if possible)
234 def __init__(self, input, fontsize, *attrs):
238 def __init__(self, input, fontsize, *attrs):
235 wx.grid.PyGridTableBase.__init__(self)
239 wx.grid.PyGridTableBase.__init__(self)
236 self.input = input
240 self.input = input
237 self.iterator = ipipe.xiter(input)
241 self.iterator = ipipe.xiter(input)
238 self.items = []
242 self.items = []
239 self.attrs = [ipipe.upgradexattr(attr) for attr in attrs]
243 self.attrs = [ipipe.upgradexattr(attr) for attr in attrs]
240 self._displayattrs = self.attrs[:]
244 self._displayattrs = self.attrs[:]
241 self._displayattrset = set(self.attrs)
245 self._displayattrset = set(self.attrs)
242 self.fontsize = fontsize
246 self.fontsize = fontsize
243 self._fetch(1)
247 self._fetch(1)
244 self.timer = wx.Timer()
248 self.timer = wx.Timer()
245 self.timer.Bind(wx.EVT_TIMER, self.refresh_content)
249 self.timer.Bind(wx.EVT_TIMER, self.refresh_content)
246
250
247 def GetAttr(self, *args):
251 def GetAttr(self, *args):
248 attr = wx.grid.GridCellAttr()
252 attr = wx.grid.GridCellAttr()
249 attr.SetFont(wx.Font(self.fontsize, wx.TELETYPE, wx.NORMAL, wx.NORMAL))
253 attr.SetFont(wx.Font(self.fontsize, wx.TELETYPE, wx.NORMAL, wx.NORMAL))
250 return attr
254 return attr
251
255
252 def GetNumberRows(self):
256 def GetNumberRows(self):
253 return len(self.items)
257 return len(self.items)
254
258
255 def GetNumberCols(self):
259 def GetNumberCols(self):
256 return len(self._displayattrs)
260 return len(self._displayattrs)
257
261
258 def GetColLabelValue(self, col):
262 def GetColLabelValue(self, col):
259 if col < len(self._displayattrs):
263 if col < len(self._displayattrs):
260 return self._displayattrs[col].name()
264 return self._displayattrs[col].name()
261 else:
265 else:
262 return ""
266 return ""
263
267
264 def GetRowLabelValue(self, row):
268 def GetRowLabelValue(self, row):
265 return str(row)
269 return str(row)
266
270
267 def IsEmptyCell(self, row, col):
271 def IsEmptyCell(self, row, col):
268 return False
272 return False
269
273
270 def _append(self, item):
274 def _append(self, item):
271 self.items.append(item)
275 self.items.append(item)
272 # Nothing to do if the set of attributes has been fixed by the user
276 # Nothing to do if the set of attributes has been fixed by the user
273 if not self.attrs:
277 if not self.attrs:
274 for attr in ipipe.xattrs(item):
278 for attr in ipipe.xattrs(item):
275 attr = ipipe.upgradexattr(attr)
279 attr = ipipe.upgradexattr(attr)
276 if attr not in self._displayattrset:
280 if attr not in self._displayattrset:
277 self._displayattrs.append(attr)
281 self._displayattrs.append(attr)
278 self._displayattrset.add(attr)
282 self._displayattrset.add(attr)
279
283
280 def _fetch(self, count):
284 def _fetch(self, count):
281 # Try to fill ``self.items`` with at least ``count`` objects.
285 # Try to fill ``self.items`` with at least ``count`` objects.
282 have = len(self.items)
286 have = len(self.items)
283 while self.iterator is not None and have < count:
287 while self.iterator is not None and have < count:
284 try:
288 try:
285 item = self.iterator.next()
289 item = self.iterator.next()
286 except StopIteration:
290 except StopIteration:
287 self.iterator = None
291 self.iterator = None
288 break
292 break
289 except (KeyboardInterrupt, SystemExit):
293 except (KeyboardInterrupt, SystemExit):
290 raise
294 raise
291 except Exception, exc:
295 except Exception, exc:
292 have += 1
296 have += 1
293 self._append(exc)
297 self._append(exc)
294 self.iterator = None
298 self.iterator = None
295 break
299 break
296 else:
300 else:
297 have += 1
301 have += 1
298 self._append(item)
302 self._append(item)
299
303
300 def GetValue(self, row, col):
304 def GetValue(self, row, col):
301 # some kind of dummy-function: does not return anything but "";
305 # some kind of dummy-function: does not return anything but "";
302 # (The value isn't use anyway)
306 # (The value isn't use anyway)
303 # its main task is to trigger the fetch of new objects
307 # its main task is to trigger the fetch of new objects
304 sizing_needed = False
308 sizing_needed = False
305 had_cols = len(self._displayattrs)
309 had_cols = len(self._displayattrs)
306 had_rows = len(self.items)
310 had_rows = len(self.items)
307 if row == had_rows - 1 and self.iterator is not None:
311 if row == had_rows - 1 and self.iterator is not None:
308 self._fetch(row + 20)
312 self._fetch(row + 20)
309 sizing_needed = True
313 sizing_needed = True
310 have_rows = len(self.items)
314 have_rows = len(self.items)
311 have_cols = len(self._displayattrs)
315 have_cols = len(self._displayattrs)
312 if have_rows > had_rows:
316 if have_rows > had_rows:
313 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, have_rows - had_rows)
317 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, have_rows - had_rows)
314 self.GetView().ProcessTableMessage(msg)
318 self.GetView().ProcessTableMessage(msg)
315 sizing_needed = True
319 sizing_needed = True
316 if row >= have_rows:
320 if row >= have_rows:
317 return ""
321 return ""
318 if have_cols != had_cols:
322 if have_cols != had_cols:
319 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED, have_cols - had_cols)
323 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED, have_cols - had_cols)
320 self.GetView().ProcessTableMessage(msg)
324 self.GetView().ProcessTableMessage(msg)
321 sizing_needed = True
325 sizing_needed = True
322 if sizing_needed:
326 if sizing_needed:
323 self.GetView().AutoSizeColumns(False)
327 self.GetView().AutoSizeColumns(False)
324 return ""
328 return ""
325
329
326 def SetValue(self, row, col, value):
330 def SetValue(self, row, col, value):
327 pass
331 pass
328
332
329 def refresh_content(self, event):
333 def refresh_content(self, event):
330 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, 0, self.GetNumberRows())
334 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, 0, self.GetNumberRows())
331 self.GetView().ProcessTableMessage(msg)
335 self.GetView().ProcessTableMessage(msg)
332 self.iterator = ipipe.xiter(self.input)
336 self.iterator = ipipe.xiter(self.input)
333 self.items = []
337 self.items = []
334 self.attrs = [] # _append will calculate new displayattrs
338 self.attrs = [] # _append will calculate new displayattrs
335 self._fetch(1) # fetch one...
339 self._fetch(1) # fetch one...
336 if self.items:
340 if self.items:
337 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, 1)
341 msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED, 1)
338 self.GetView().ProcessTableMessage(msg)
342 self.GetView().ProcessTableMessage(msg)
339 self.GetValue(0, 0) # and trigger "fetch next 20"
343 self.GetValue(0, 0) # and trigger "fetch next 20"
340 item = self.items[0]
344 item = self.items[0]
341 self.GetView().AutoSizeColumns(False)
345 self.GetView().AutoSizeColumns(False)
342 panel = self.GetView().GetParent()
346 panel = self.GetView().GetParent()
343 nb = panel.GetParent()
347 nb = panel.GetParent()
344 current = nb.GetSelection()
348 current = nb.GetSelection()
345 if nb.GetPage(current) == panel:
349 if nb.GetPage(current) == panel:
346 self.GetView().set_footer(item)
350 self.GetView().set_footer(item)
347
351
348 class IGridGrid(wx.grid.Grid):
352 class IGridGrid(wx.grid.Grid):
349 # The actual grid
353 # The actual grid
350 # all methods for selecting/sorting/picking/... data are implemented here
354 # all methods for selecting/sorting/picking/... data are implemented here
351 def __init__(self, panel, input, *attrs):
355 def __init__(self, panel, input, *attrs):
352 wx.grid.Grid.__init__(self, panel)
356 wx.grid.Grid.__init__(self, panel)
353 fontsize = 9
357 fontsize = 9
354 self.input = input
358 self.input = input
355 self.table = IGridTable(self.input, fontsize, *attrs)
359 self.table = IGridTable(self.input, fontsize, *attrs)
356 self.SetTable(self.table, True)
360 self.SetTable(self.table, True)
357 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
361 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
358 self.SetDefaultRenderer(IGridRenderer(self.table))
362 self.SetDefaultRenderer(IGridRenderer(self.table))
359 self.EnableEditing(False)
363 self.EnableEditing(False)
360 self.Bind(wx.EVT_KEY_DOWN, self.key_pressed)
364 self.Bind(wx.EVT_KEY_DOWN, self.key_pressed)
361 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.cell_doubleclicked)
365 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.cell_doubleclicked)
362 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.cell_leftclicked)
366 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.cell_leftclicked)
363 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_DCLICK, self.label_doubleclicked)
367 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_DCLICK, self.label_doubleclicked)
364 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_label_leftclick)
368 self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_label_leftclick)
365 self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self._on_selected_range)
369 self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self._on_selected_range)
366 self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_selected_cell)
370 self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self._on_selected_cell)
367 self.current_selection = set()
371 self.current_selection = set()
368 self.maxchars = 200
372 self.maxchars = 200
369
373
370 def on_label_leftclick(self, event):
374 def on_label_leftclick(self, event):
371 event.Skip()
375 event.Skip()
372
376
373 def error_output(self, text):
377 def error_output(self, text):
374 wx.Bell()
378 wx.Bell()
375 frame = self.GetParent().GetParent().GetParent()
379 frame = self.GetParent().GetParent().GetParent()
376 frame.SetStatusText(str(text))
380 frame.SetStatusText(str(text))
377
381
378 def _on_selected_range(self, event):
382 def _on_selected_range(self, event):
379 # Internal update to the selection tracking lists
383 # Internal update to the selection tracking lists
380 if event.Selecting():
384 if event.Selecting():
381 # adding to the list...
385 # adding to the list...
382 self.current_selection.update(xrange(event.GetTopRow(), event.GetBottomRow()+1))
386 self.current_selection.update(xrange(event.GetTopRow(), event.GetBottomRow()+1))
383 else:
387 else:
384 # removal from list
388 # removal from list
385 for index in xrange(event.GetTopRow(), event.GetBottomRow()+1):
389 for index in xrange(event.GetTopRow(), event.GetBottomRow()+1):
386 self.current_selection.discard(index)
390 self.current_selection.discard(index)
387 event.Skip()
391 event.Skip()
388
392
389 def _on_selected_cell(self, event):
393 def _on_selected_cell(self, event):
390 # Internal update to the selection tracking list
394 # Internal update to the selection tracking list
391 self.current_selection = set([event.GetRow()])
395 self.current_selection = set([event.GetRow()])
392 event.Skip()
396 event.Skip()
393
397
394 def sort(self, key, reverse=False):
398 def sort(self, key, reverse=False):
395 """
399 """
396 Sort the current list of items using the key function ``key``. If
400 Sort the current list of items using the key function ``key``. If
397 ``reverse`` is true the sort order is reversed.
401 ``reverse`` is true the sort order is reversed.
398 """
402 """
399 row = self.GetGridCursorRow()
403 row = self.GetGridCursorRow()
400 col = self.GetGridCursorCol()
404 col = self.GetGridCursorCol()
401 curitem = self.table.items[row] # Remember where the cursor is now
405 curitem = self.table.items[row] # Remember where the cursor is now
402 # Sort items
406 # Sort items
403 def realkey(item):
407 def realkey(item):
404 try:
408 try:
405 return key(item)
409 return key(item)
406 except (KeyboardInterrupt, SystemExit):
410 except (KeyboardInterrupt, SystemExit):
407 raise
411 raise
408 except Exception:
412 except Exception:
409 return None
413 return None
410 try:
414 try:
411 self.table.items = ipipe.deque(sorted(self.table.items, key=realkey, reverse=reverse))
415 self.table.items = ipipe.deque(sorted(self.table.items, key=realkey, reverse=reverse))
412 except TypeError, exc:
416 except TypeError, exc:
413 self.error_output("Exception encountered: %s" % exc)
417 self.error_output("Exception encountered: %s" % exc)
414 return
418 return
415 # Find out where the object under the cursor went
419 # Find out where the object under the cursor went
416 for (i, item) in enumerate(self.table.items):
420 for (i, item) in enumerate(self.table.items):
417 if item is curitem:
421 if item is curitem:
418 self.SetGridCursor(i,col)
422 self.SetGridCursor(i,col)
419 self.MakeCellVisible(i,col)
423 self.MakeCellVisible(i,col)
420 self.Refresh()
424 self.Refresh()
421
425
422 def sortattrasc(self):
426 def sortattrasc(self):
423 """
427 """
424 Sort in ascending order; sorting criteria is the current attribute
428 Sort in ascending order; sorting criteria is the current attribute
425 """
429 """
426 col = self.GetGridCursorCol()
430 col = self.GetGridCursorCol()
427 attr = self.table._displayattrs[col]
431 attr = self.table._displayattrs[col]
428 frame = self.GetParent().GetParent().GetParent()
432 frame = self.GetParent().GetParent().GetParent()
429 if attr is ipipe.noitem:
433 if attr is ipipe.noitem:
430 self.error_output("no column under cursor")
434 self.error_output("no column under cursor")
431 return
435 return
432 frame.SetStatusText("sort by %s (ascending)" % attr.name())
436 frame.SetStatusText("sort by %s (ascending)" % attr.name())
433 def key(item):
437 def key(item):
434 try:
438 try:
435 return attr.value(item)
439 return attr.value(item)
436 except (KeyboardInterrupt, SystemExit):
440 except (KeyboardInterrupt, SystemExit):
437 raise
441 raise
438 except Exception:
442 except Exception:
439 return None
443 return None
440 self.sort(key)
444 self.sort(key)
441
445
442 def sortattrdesc(self):
446 def sortattrdesc(self):
443 """
447 """
444 Sort in descending order; sorting criteria is the current attribute
448 Sort in descending order; sorting criteria is the current attribute
445 """
449 """
446 col = self.GetGridCursorCol()
450 col = self.GetGridCursorCol()
447 attr = self.table._displayattrs[col]
451 attr = self.table._displayattrs[col]
448 frame = self.GetParent().GetParent().GetParent()
452 frame = self.GetParent().GetParent().GetParent()
449 if attr is ipipe.noitem:
453 if attr is ipipe.noitem:
450 self.error_output("no column under cursor")
454 self.error_output("no column under cursor")
451 return
455 return
452 frame.SetStatusText("sort by %s (descending)" % attr.name())
456 frame.SetStatusText("sort by %s (descending)" % attr.name())
453 def key(item):
457 def key(item):
454 try:
458 try:
455 return attr.value(item)
459 return attr.value(item)
456 except (KeyboardInterrupt, SystemExit):
460 except (KeyboardInterrupt, SystemExit):
457 raise
461 raise
458 except Exception:
462 except Exception:
459 return None
463 return None
460 self.sort(key, reverse=True)
464 self.sort(key, reverse=True)
461
465
462 def label_doubleclicked(self, event):
466 def label_doubleclicked(self, event):
463 row = event.GetRow()
467 row = event.GetRow()
464 col = event.GetCol()
468 col = event.GetCol()
465 if col == -1:
469 if col == -1:
466 self.enter(row)
470 self.enter(row)
467
471
468 def _getvalue(self, row, col):
472 def _getvalue(self, row, col):
469 """
473 """
470 Gets the text which is displayed at ``(row, col)``
474 Gets the text which is displayed at ``(row, col)``
471 """
475 """
472 try:
476 try:
473 value = self.table._displayattrs[col].value(self.table.items[row])
477 value = self.table._displayattrs[col].value(self.table.items[row])
474 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
478 (align, width, text) = ipipe.xformat(value, "cell", self.maxchars)
475 except IndexError:
479 except IndexError:
476 raise IndexError
480 raise IndexError
477 except Exception, exc:
481 except Exception, exc:
478 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
482 (align, width, text) = ipipe.xformat(exc, "cell", self.maxchars)
479 return text
483 return text
480
484
481 def searchexpression(self, searchexp, startrow=None, search_forward=True ):
485 def searchexpression(self, searchexp, startrow=None, search_forward=True ):
482 """
486 """
483 Find by expression
487 Find by expression
484 """
488 """
485 frame = self.GetParent().GetParent().GetParent()
489 frame = self.GetParent().GetParent().GetParent()
486 if searchexp:
490 if searchexp:
487 if search_forward:
491 if search_forward:
488 if not startrow:
492 if not startrow:
489 row = self.GetGridCursorRow()+1
493 row = self.GetGridCursorRow()+1
490 else:
494 else:
491 row = startrow + 1
495 row = startrow + 1
492 while True:
496 while True:
493 try:
497 try:
494 foo = self.table.GetValue(row, 0)
498 foo = self.table.GetValue(row, 0)
495 item = self.table.items[row]
499 item = self.table.items[row]
496 try:
500 try:
497 globals = ipipe.getglobals(None)
501 globals = ipipe.getglobals(None)
498 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
502 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
499 self.SetGridCursor(row, 0) # found something
503 self.SetGridCursor(row, 0) # found something
500 self.MakeCellVisible(row, 0)
504 self.MakeCellVisible(row, 0)
501 break
505 break
502 except (KeyboardInterrupt, SystemExit):
506 except (KeyboardInterrupt, SystemExit):
503 raise
507 raise
504 except Exception, exc:
508 except Exception, exc:
505 frame.SetStatusText(str(exc))
509 frame.SetStatusText(str(exc))
506 wx.Bell()
510 wx.Bell()
507 break # break on error
511 break # break on error
508 except IndexError:
512 except IndexError:
509 return
513 return
510 row += 1
514 row += 1
511 else:
515 else:
512 if not startrow:
516 if not startrow:
513 row = self.GetGridCursorRow() - 1
517 row = self.GetGridCursorRow() - 1
514 else:
518 else:
515 row = startrow - 1
519 row = startrow - 1
516 while True:
520 while True:
517 try:
521 try:
518 foo = self.table.GetValue(row, 0)
522 foo = self.table.GetValue(row, 0)
519 item = self.table.items[row]
523 item = self.table.items[row]
520 try:
524 try:
521 globals = ipipe.getglobals(None)
525 globals = ipipe.getglobals(None)
522 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
526 if eval(searchexp, globals, ipipe.AttrNamespace(item)):
523 self.SetGridCursor(row, 0) # found something
527 self.SetGridCursor(row, 0) # found something
524 self.MakeCellVisible(row, 0)
528 self.MakeCellVisible(row, 0)
525 break
529 break
526 except (KeyboardInterrupt, SystemExit):
530 except (KeyboardInterrupt, SystemExit):
527 raise
531 raise
528 except Exception, exc:
532 except Exception, exc:
529 frame.SetStatusText(str(exc))
533 frame.SetStatusText(str(exc))
530 wx.Bell()
534 wx.Bell()
531 break # break on error
535 break # break on error
532 except IndexError:
536 except IndexError:
533 return
537 return
534 row -= 1
538 row -= 1
535
539
536
540
537 def search(self, searchtext, startrow=None, startcol=None, search_forward=True):
541 def search(self, searchtext, startrow=None, startcol=None, search_forward=True):
538 """
542 """
539 search for ``searchtext``, starting in ``(startrow, startcol)``;
543 search for ``searchtext``, starting in ``(startrow, startcol)``;
540 if ``search_forward`` is true the direction is "forward"
544 if ``search_forward`` is true the direction is "forward"
541 """
545 """
542 searchtext = searchtext.lower()
546 searchtext = searchtext.lower()
543 if search_forward:
547 if search_forward:
544 if startrow is not None and startcol is not None:
548 if startrow is not None and startcol is not None:
545 row = startrow
549 row = startrow
546 else:
550 else:
547 startcol = self.GetGridCursorCol() + 1
551 startcol = self.GetGridCursorCol() + 1
548 row = self.GetGridCursorRow()
552 row = self.GetGridCursorRow()
549 if startcol >= self.GetNumberCols():
553 if startcol >= self.GetNumberCols():
550 startcol = 0
554 startcol = 0
551 row += 1
555 row += 1
552 while True:
556 while True:
553 for col in xrange(startcol, self.table.GetNumberCols()):
557 for col in xrange(startcol, self.table.GetNumberCols()):
554 try:
558 try:
555 foo = self.table.GetValue(row, col)
559 foo = self.table.GetValue(row, col)
556 text = self._getvalue(row, col)
560 text = self._getvalue(row, col)
557 if searchtext in text.string().lower():
561 if searchtext in text.string().lower():
558 self.SetGridCursor(row, col)
562 self.SetGridCursor(row, col)
559 self.MakeCellVisible(row, col)
563 self.MakeCellVisible(row, col)
560 return
564 return
561 except IndexError:
565 except IndexError:
562 return
566 return
563 startcol = 0
567 startcol = 0
564 row += 1
568 row += 1
565 else:
569 else:
566 if startrow is not None and startcol is not None:
570 if startrow is not None and startcol is not None:
567 row = startrow
571 row = startrow
568 else:
572 else:
569 startcol = self.GetGridCursorCol() - 1
573 startcol = self.GetGridCursorCol() - 1
570 row = self.GetGridCursorRow()
574 row = self.GetGridCursorRow()
571 if startcol < 0:
575 if startcol < 0:
572 startcol = self.GetNumberCols() - 1
576 startcol = self.GetNumberCols() - 1
573 row -= 1
577 row -= 1
574 while True:
578 while True:
575 for col in xrange(startcol, -1, -1):
579 for col in xrange(startcol, -1, -1):
576 try:
580 try:
577 foo = self.table.GetValue(row, col)
581 foo = self.table.GetValue(row, col)
578 text = self._getvalue(row, col)
582 text = self._getvalue(row, col)
579 if searchtext in text.string().lower():
583 if searchtext in text.string().lower():
580 self.SetGridCursor(row, col)
584 self.SetGridCursor(row, col)
581 self.MakeCellVisible(row, col)
585 self.MakeCellVisible(row, col)
582 return
586 return
583 except IndexError:
587 except IndexError:
584 return
588 return
585 startcol = self.table.GetNumberCols()-1
589 startcol = self.table.GetNumberCols()-1
586 row -= 1
590 row -= 1
587
591
588 def key_pressed(self, event):
592 def key_pressed(self, event):
589 """
593 """
590 Maps pressed keys to functions
594 Maps pressed keys to functions
591 """
595 """
592 frame = self.GetParent().GetParent().GetParent()
596 frame = self.GetParent().GetParent().GetParent()
593 frame.SetStatusText("")
597 frame.SetStatusText("")
594 sh = event.ShiftDown()
598 sh = event.ShiftDown()
595 ctrl = event.ControlDown()
599 ctrl = event.ControlDown()
596
600
597 keycode = event.GetKeyCode()
601 keycode = event.GetKeyCode()
598 if keycode == ord("P"):
602 if keycode == ord("P"):
599 row = self.GetGridCursorRow()
603 row = self.GetGridCursorRow()
600 if sh:
604 if sh:
601 col = self.GetGridCursorCol()
605 col = self.GetGridCursorCol()
602 self.pickattr(row, col)
606 self.pickattr(row, col)
603 else:
607 else:
604 self.pick(row)
608 self.pick(row)
605 elif keycode == ord("M"):
609 elif keycode == ord("M"):
606 if ctrl:
610 if ctrl:
607 col = self.GetGridCursorCol()
611 col = self.GetGridCursorCol()
608 self.pickrowsattr(sorted(self.current_selection), col)
612 self.pickrowsattr(sorted(self.current_selection), col)
609 else:
613 else:
610 self.pickrows(sorted(self.current_selection))
614 self.pickrows(sorted(self.current_selection))
611 elif keycode in (wx.WXK_BACK, wx.WXK_DELETE, ord("X")) and not (ctrl or sh):
615 elif keycode in (wx.WXK_BACK, wx.WXK_DELETE, ord("X")) and not (ctrl or sh):
612 self.delete_current_notebook()
616 self.delete_current_notebook()
613 elif keycode in (ord("E"), ord("\r")):
617 elif keycode in (ord("E"), ord("\r")):
614 row = self.GetGridCursorRow()
618 row = self.GetGridCursorRow()
615 if sh:
619 if sh:
616 col = self.GetGridCursorCol()
620 col = self.GetGridCursorCol()
617 self.enterattr(row, col)
621 self.enterattr(row, col)
618 else:
622 else:
619 self.enter(row)
623 self.enter(row)
620 elif keycode == ord("E") and ctrl:
624 elif keycode == ord("E") and ctrl:
621 row = self.GetGridCursorRow()
625 row = self.GetGridCursorRow()
622 self.SetGridCursor(row, self.GetNumberCols()-1)
626 self.SetGridCursor(row, self.GetNumberCols()-1)
623 elif keycode == wx.WXK_HOME or (keycode == ord("A") and ctrl):
627 elif keycode == wx.WXK_HOME or (keycode == ord("A") and ctrl):
624 row = self.GetGridCursorRow()
628 row = self.GetGridCursorRow()
625 self.SetGridCursor(row, 0)
629 self.SetGridCursor(row, 0)
626 elif keycode == ord("C") and sh:
630 elif keycode == ord("C") and sh:
627 col = self.GetGridCursorCol()
631 col = self.GetGridCursorCol()
628 attr = self.table._displayattrs[col]
632 attr = self.table._displayattrs[col]
629 result = []
633 result = []
630 for i in xrange(self.GetNumberRows()):
634 for i in xrange(self.GetNumberRows()):
631 result.append(self.table._displayattrs[col].value(self.table.items[i]))
635 result.append(self.table._displayattrs[col].value(self.table.items[i]))
632 self.quit(result)
636 self.quit(result)
633 elif keycode in (wx.WXK_ESCAPE, ord("Q")) and not (ctrl or sh):
637 elif keycode in (wx.WXK_ESCAPE, ord("Q")) and not (ctrl or sh):
634 self.quit()
638 self.quit()
635 elif keycode == ord("<"):
639 elif keycode == ord("<"):
636 row = self.GetGridCursorRow()
640 row = self.GetGridCursorRow()
637 col = self.GetGridCursorCol()
641 col = self.GetGridCursorCol()
638 if not event.ShiftDown():
642 if not event.ShiftDown():
639 newcol = col - 1
643 newcol = col - 1
640 if newcol >= 0:
644 if newcol >= 0:
641 self.SetGridCursor(row, col - 1)
645 self.SetGridCursor(row, col - 1)
642 else:
646 else:
643 newcol = col + 1
647 newcol = col + 1
644 if newcol < self.GetNumberCols():
648 if newcol < self.GetNumberCols():
645 self.SetGridCursor(row, col + 1)
649 self.SetGridCursor(row, col + 1)
646 elif keycode == ord("D"):
650 elif keycode == ord("D"):
647 col = self.GetGridCursorCol()
651 col = self.GetGridCursorCol()
648 row = self.GetGridCursorRow()
652 row = self.GetGridCursorRow()
649 if not sh:
653 if not sh:
650 self.detail(row, col)
654 self.detail(row, col)
651 else:
655 else:
652 self.detail_attr(row, col)
656 self.detail_attr(row, col)
653 elif keycode == ord("F") and ctrl:
657 elif keycode == ord("F") and ctrl:
654 if sh:
658 if sh:
655 frame.enter_searchexpression(event)
659 frame.enter_searchexpression(event)
656 else:
660 else:
657 frame.enter_searchtext(event)
661 frame.enter_searchtext(event)
658 elif keycode == wx.WXK_F3:
662 elif keycode == wx.WXK_F3:
659 if sh:
663 if sh:
660 frame.find_previous(event)
664 frame.find_previous(event)
661 else:
665 else:
662 frame.find_next(event)
666 frame.find_next(event)
663 elif keycode == ord("V"):
667 elif keycode == ord("V"):
664 if sh:
668 if sh:
665 self.sortattrdesc()
669 self.sortattrdesc()
666 else:
670 else:
667 self.sortattrasc()
671 self.sortattrasc()
668 elif keycode == wx.WXK_DOWN:
672 elif keycode == wx.WXK_DOWN:
669 row = self.GetGridCursorRow()
673 row = self.GetGridCursorRow()
670 try:
674 try:
671 item = self.table.items[row+1]
675 item = self.table.items[row+1]
672 except IndexError:
676 except IndexError:
673 item = self.table.items[row]
677 item = self.table.items[row]
674 self.set_footer(item)
678 self.set_footer(item)
675 event.Skip()
679 event.Skip()
676 elif keycode == wx.WXK_UP:
680 elif keycode == wx.WXK_UP:
677 row = self.GetGridCursorRow()
681 row = self.GetGridCursorRow()
678 if row >= 1:
682 if row >= 1:
679 item = self.table.items[row-1]
683 item = self.table.items[row-1]
680 else:
684 else:
681 item = self.table.items[row]
685 item = self.table.items[row]
682 self.set_footer(item)
686 self.set_footer(item)
683 event.Skip()
687 event.Skip()
684 elif keycode == wx.WXK_RIGHT:
688 elif keycode == wx.WXK_RIGHT:
685 row = self.GetGridCursorRow()
689 row = self.GetGridCursorRow()
686 item = self.table.items[row]
690 item = self.table.items[row]
687 self.set_footer(item)
691 self.set_footer(item)
688 event.Skip()
692 event.Skip()
689 elif keycode == wx.WXK_LEFT:
693 elif keycode == wx.WXK_LEFT:
690 row = self.GetGridCursorRow()
694 row = self.GetGridCursorRow()
691 item = self.table.items[row]
695 item = self.table.items[row]
692 self.set_footer(item)
696 self.set_footer(item)
693 event.Skip()
697 event.Skip()
694 elif keycode == ord("R") or keycode == wx.WXK_F5:
698 elif keycode == ord("R") or keycode == wx.WXK_F5:
695 self.table.refresh_content(event)
699 self.table.refresh_content(event)
696 elif keycode == ord("I"):
700 elif keycode == ord("I"):
697 row = self.GetGridCursorRow()
701 row = self.GetGridCursorRow()
698 if not sh:
702 if not sh:
699 self.pickinput(row)
703 self.pickinput(row)
700 else:
704 else:
701 col = self.GetGridCursorCol()
705 col = self.GetGridCursorCol()
702 self.pickinputattr(row, col)
706 self.pickinputattr(row, col)
703 else:
707 else:
704 event.Skip()
708 event.Skip()
705
709
706 def delete_current_notebook(self):
710 def delete_current_notebook(self):
707 """
711 """
708 deletes the current notebook tab
712 deletes the current notebook tab
709 """
713 """
710 panel = self.GetParent()
714 panel = self.GetParent()
711 nb = panel.GetParent()
715 nb = panel.GetParent()
712 current = nb.GetSelection()
716 current = nb.GetSelection()
713 count = nb.GetPageCount()
717 count = nb.GetPageCount()
714 if count > 1:
718 if count > 1:
715 for i in xrange(count-1, current-1, -1):
719 for i in xrange(count-1, current-1, -1):
716 nb.DeletePage(i)
720 nb.DeletePage(i)
717 nb.GetCurrentPage().grid.SetFocus()
721 nb.GetCurrentPage().grid.SetFocus()
718 else:
722 else:
719 frame = nb.GetParent()
723 frame = nb.GetParent()
720 frame.SetStatusText("This is the last level!")
724 frame.SetStatusText("This is the last level!")
721
725
722 def _doenter(self, value, *attrs):
726 def _doenter(self, value, *attrs):
723 """
727 """
724 "enter" a special item resulting in a new notebook tab
728 "enter" a special item resulting in a new notebook tab
725 """
729 """
726 panel = self.GetParent()
730 panel = self.GetParent()
727 nb = panel.GetParent()
731 nb = panel.GetParent()
728 frame = nb.GetParent()
732 frame = nb.GetParent()
729 current = nb.GetSelection()
733 current = nb.GetSelection()
730 count = nb.GetPageCount()
734 count = nb.GetPageCount()
731 try: # if we want to enter something non-iterable, e.g. a function
735 try: # if we want to enter something non-iterable, e.g. a function
732 if current + 1 == count and value is not self.input: # we have an event in the last tab
736 if current + 1 == count and value is not self.input: # we have an event in the last tab
733 frame._add_notebook(value, *attrs)
737 frame._add_notebook(value, *attrs)
734 elif value != self.input: # we have to delete all tabs newer than [panel] first
738 elif value != self.input: # we have to delete all tabs newer than [panel] first
735 for i in xrange(count-1, current, -1): # some tabs don't close if we don't close in *reverse* order
739 for i in xrange(count-1, current, -1): # some tabs don't close if we don't close in *reverse* order
736 nb.DeletePage(i)
740 nb.DeletePage(i)
737 frame._add_notebook(value)
741 frame._add_notebook(value)
738 except TypeError, exc:
742 except TypeError, exc:
739 if exc.__class__.__module__ == "exceptions":
743 if exc.__class__.__module__ == "exceptions":
740 msg = "%s: %s" % (exc.__class__.__name__, exc)
744 msg = "%s: %s" % (exc.__class__.__name__, exc)
741 else:
745 else:
742 msg = "%s.%s: %s" % (exc.__class__.__module__, exc.__class__.__name__, exc)
746 msg = "%s.%s: %s" % (exc.__class__.__module__, exc.__class__.__name__, exc)
743 frame.SetStatusText(msg)
747 frame.SetStatusText(msg)
744
748
745 def enterattr(self, row, col):
749 def enterattr(self, row, col):
746 try:
750 try:
747 attr = self.table._displayattrs[col]
751 attr = self.table._displayattrs[col]
748 value = attr.value(self.table.items[row])
752 value = attr.value(self.table.items[row])
749 except Exception, exc:
753 except Exception, exc:
750 self.error_output(str(exc))
754 self.error_output(str(exc))
751 else:
755 else:
752 self._doenter(value)
756 self._doenter(value)
753
757
754 def set_footer(self, item):
758 def set_footer(self, item):
755 frame = self.GetParent().GetParent().GetParent()
759 frame = self.GetParent().GetParent().GetParent()
756 frame.SetStatusText(" ".join([str(text) for (style, text) in ipipe.xformat(item, "footer", 20)[2]]), 0)
760 frame.SetStatusText(" ".join([str(text) for (style, text) in ipipe.xformat(item, "footer", 20)[2]]), 0)
757
761
758 def enter(self, row):
762 def enter(self, row):
759 try:
763 try:
760 value = self.table.items[row]
764 value = self.table.items[row]
761 except Exception, exc:
765 except Exception, exc:
762 self.error_output(str(exc))
766 self.error_output(str(exc))
763 else:
767 else:
764 self._doenter(value)
768 self._doenter(value)
765
769
766 def detail(self, row, col):
770 def detail(self, row, col):
767 """
771 """
768 shows a detail-view of the current cell
772 shows a detail-view of the current cell
769 """
773 """
770 try:
774 try:
771 attr = self.table._displayattrs[col]
775 attr = self.table._displayattrs[col]
772 item = self.table.items[row]
776 item = self.table.items[row]
773 except Exception, exc:
777 except Exception, exc:
774 self.error_output(str(exc))
778 self.error_output(str(exc))
775 else:
779 else:
776 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
780 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
777 self._doenter(attrs)
781 self._doenter(attrs)
778
782
779 def detail_attr(self, row, col):
783 def detail_attr(self, row, col):
780 try:
784 try:
781 attr = self.table._displayattrs[col]
785 attr = self.table._displayattrs[col]
782 item = attr.value(self.table.items[row])
786 item = attr.value(self.table.items[row])
783 except Exception, exc:
787 except Exception, exc:
784 self.error_output(str(exc))
788 self.error_output(str(exc))
785 else:
789 else:
786 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
790 attrs = [ipipe.AttributeDetail(item, attr) for attr in ipipe.xattrs(item, "detail")]
787 self._doenter(attrs)
791 self._doenter(attrs)
788
792
789 def quit(self, result=None):
793 def quit(self, result=None):
790 """
794 """
791 quit
795 quit
792 """
796 """
793 frame = self.GetParent().GetParent().GetParent()
797 frame = self.GetParent().GetParent().GetParent()
794 if frame.helpdialog:
798 if frame.helpdialog:
795 frame.helpdialog.Destroy()
799 frame.helpdialog.Destroy()
796 app = frame.parent
800 app = frame.parent
797 if app is not None:
801 if app is not None:
798 app.result = result
802 app.result = result
799 frame.Close()
803 frame.Close()
800 frame.Destroy()
804 frame.Destroy()
801
805
802 def cell_doubleclicked(self, event):
806 def cell_doubleclicked(self, event):
803 self.enterattr(event.GetRow(), event.GetCol())
807 self.enterattr(event.GetRow(), event.GetCol())
804 event.Skip()
808 event.Skip()
805
809
806 def cell_leftclicked(self, event):
810 def cell_leftclicked(self, event):
807 row = event.GetRow()
811 row = event.GetRow()
808 item = self.table.items[row]
812 item = self.table.items[row]
809 self.set_footer(item)
813 self.set_footer(item)
810 event.Skip()
814 event.Skip()
811
815
812 def pick(self, row):
816 def pick(self, row):
813 """
817 """
814 pick a single row and return to the IPython prompt
818 pick a single row and return to the IPython prompt
815 """
819 """
816 try:
820 try:
817 value = self.table.items[row]
821 value = self.table.items[row]
818 except Exception, exc:
822 except Exception, exc:
819 self.error_output(str(exc))
823 self.error_output(str(exc))
820 else:
824 else:
821 self.quit(value)
825 self.quit(value)
822
826
823 def pickinput(self, row):
827 def pickinput(self, row):
824 try:
828 try:
825 value = self.table.items[row]
829 value = self.table.items[row]
826 except Exception, exc:
830 except Exception, exc:
827 self.error_output(str(exc))
831 self.error_output(str(exc))
828 else:
832 else:
829 api = ipapi.get()
833 api = ipapi.get()
830 api.set_next_input(str(value))
834 api.set_next_input(str(value))
831 self.quit(value)
835 self.quit(value)
832
836
833 def pickinputattr(self, row, col):
837 def pickinputattr(self, row, col):
834 try:
838 try:
835 attr = self.table._displayattrs[col]
839 attr = self.table._displayattrs[col]
836 value = attr.value(self.table.items[row])
840 value = attr.value(self.table.items[row])
837 except Exception, exc:
841 except Exception, exc:
838 self.error_output(str(exc))
842 self.error_output(str(exc))
839 else:
843 else:
840 api = ipapi.get()
844 api = ipapi.get()
841 api.set_next_input(str(value))
845 api.set_next_input(str(value))
842 self.quit(value)
846 self.quit(value)
843
847
844 def pickrows(self, rows):
848 def pickrows(self, rows):
845 """
849 """
846 pick multiple rows and return to the IPython prompt
850 pick multiple rows and return to the IPython prompt
847 """
851 """
848 try:
852 try:
849 value = [self.table.items[row] for row in rows]
853 value = [self.table.items[row] for row in rows]
850 except Exception, exc:
854 except Exception, exc:
851 self.error_output(str(exc))
855 self.error_output(str(exc))
852 else:
856 else:
853 self.quit(value)
857 self.quit(value)
854
858
855 def pickrowsattr(self, rows, col):
859 def pickrowsattr(self, rows, col):
856 """"
860 """"
857 pick one column from multiple rows
861 pick one column from multiple rows
858 """
862 """
859 values = []
863 values = []
860 try:
864 try:
861 attr = self.table._displayattrs[col]
865 attr = self.table._displayattrs[col]
862 for row in rows:
866 for row in rows:
863 try:
867 try:
864 values.append(attr.value(self.table.items[row]))
868 values.append(attr.value(self.table.items[row]))
865 except (SystemExit, KeyboardInterrupt):
869 except (SystemExit, KeyboardInterrupt):
866 raise
870 raise
867 except Exception:
871 except Exception:
868 raise #pass
872 raise #pass
869 except Exception, exc:
873 except Exception, exc:
870 self.error_output(str(exc))
874 self.error_output(str(exc))
871 else:
875 else:
872 self.quit(values)
876 self.quit(values)
873
877
874 def pickattr(self, row, col):
878 def pickattr(self, row, col):
875 try:
879 try:
876 attr = self.table._displayattrs[col]
880 attr = self.table._displayattrs[col]
877 value = attr.value(self.table.items[row])
881 value = attr.value(self.table.items[row])
878 except Exception, exc:
882 except Exception, exc:
879 self.error_output(str(exc))
883 self.error_output(str(exc))
880 else:
884 else:
881 self.quit(value)
885 self.quit(value)
882
886
883
887
884 class IGridPanel(wx.Panel):
888 class IGridPanel(wx.Panel):
885 # Each IGridPanel contains an IGridGrid
889 # Each IGridPanel contains an IGridGrid
886 def __init__(self, parent, input, *attrs):
890 def __init__(self, parent, input, *attrs):
887 wx.Panel.__init__(self, parent, -1)
891 wx.Panel.__init__(self, parent, -1)
888 self.grid = IGridGrid(self, input, *attrs)
892 self.grid = IGridGrid(self, input, *attrs)
889 self.grid.FitInside()
893 self.grid.FitInside()
890 sizer = wx.BoxSizer(wx.VERTICAL)
894 sizer = wx.BoxSizer(wx.VERTICAL)
891 sizer.Add(self.grid, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
895 sizer.Add(self.grid, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)
892 self.SetSizer(sizer)
896 self.SetSizer(sizer)
893 sizer.Fit(self)
897 sizer.Fit(self)
894 sizer.SetSizeHints(self)
898 sizer.SetSizeHints(self)
895
899
896
900
897 class IGridHTMLHelp(wx.Frame):
901 class IGridHTMLHelp(wx.Frame):
898 def __init__(self, parent, title, size):
902 def __init__(self, parent, title, size):
899 wx.Frame.__init__(self, parent, -1, title, size=size)
903 wx.Frame.__init__(self, parent, -1, title, size=size)
900 html = wx.html.HtmlWindow(self)
904 html = wx.html.HtmlWindow(self)
901 if "gtk2" in wx.PlatformInfo:
905 if "gtk2" in wx.PlatformInfo:
902 html.SetStandardFonts()
906 html.SetStandardFonts()
903 html.SetPage(help)
907 html.SetPage(help)
904
908
905
909
906 class IGridFrame(wx.Frame):
910 class IGridFrame(wx.Frame):
907 maxtitlelen = 30
911 maxtitlelen = 30
908
912
909 def __init__(self, parent, input):
913 def __init__(self, parent, input):
910 title = " ".join([str(text) for (style, text) in ipipe.xformat(input, "header", 20)[2]])
914 title = " ".join([str(text) for (style, text) in ipipe.xformat(input, "header", 20)[2]])
911 wx.Frame.__init__(self, None, title=title, size=(640, 480))
915 wx.Frame.__init__(self, None, title=title, size=(640, 480))
912 self.menubar = wx.MenuBar()
916 self.menubar = wx.MenuBar()
913 self.menucounter = 100
917 self.menucounter = 100
914 self.m_help = wx.Menu()
918 self.m_help = wx.Menu()
915 self.m_search = wx.Menu()
919 self.m_search = wx.Menu()
916 self.m_sort = wx.Menu()
920 self.m_sort = wx.Menu()
917 self.m_refresh = wx.Menu()
921 self.m_refresh = wx.Menu()
918 self.notebook = wx.Notebook(self, -1, style=0)
922 self.notebook = wx.Notebook(self, -1, style=0)
919 self.statusbar = self.CreateStatusBar(1, wx.ST_SIZEGRIP)
923 self.statusbar = self.CreateStatusBar(1, wx.ST_SIZEGRIP)
920 self.statusbar.SetFieldsCount(2)
924 self.statusbar.SetFieldsCount(2)
921 self.SetStatusWidths([-1, 200])
925 self.SetStatusWidths([-1, 200])
922 self.parent = parent
926 self.parent = parent
923 self._add_notebook(input)
927 self._add_notebook(input)
924 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
928 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
925 self.makemenu(self.m_sort, "&Sort (asc)\tV", "Sort ascending", self.sortasc)
929 self.makemenu(self.m_sort, "&Sort (asc)\tV", "Sort ascending", self.sortasc)
926 self.makemenu(self.m_sort, "Sort (&desc)\tShift-V", "Sort descending", self.sortdesc)
930 self.makemenu(self.m_sort, "Sort (&desc)\tShift-V", "Sort descending", self.sortdesc)
927 self.makemenu(self.m_help, "&Help\tF1", "Help", self.display_help)
931 self.makemenu(self.m_help, "&Help\tF1", "Help", self.display_help)
928 # self.makemenu(self.m_help, "&Show help in browser", "Show help in browser", self.display_help_in_browser)
932 # self.makemenu(self.m_help, "&Show help in browser", "Show help in browser", self.display_help_in_browser)
929 self.makemenu(self.m_search, "&Find text\tCTRL-F", "Find text", self.enter_searchtext)
933 self.makemenu(self.m_search, "&Find text\tCTRL-F", "Find text", self.enter_searchtext)
930 self.makemenu(self.m_search, "Find by &expression\tCTRL-Shift-F", "Find by expression", self.enter_searchexpression)
934 self.makemenu(self.m_search, "Find by &expression\tCTRL-Shift-F", "Find by expression", self.enter_searchexpression)
931 self.makemenu(self.m_search, "Find &next\tF3", "Find next", self.find_next)
935 self.makemenu(self.m_search, "Find &next\tF3", "Find next", self.find_next)
932 self.makemenu(self.m_search, "Find &previous\tShift-F3", "Find previous", self.find_previous)
936 self.makemenu(self.m_search, "Find &previous\tShift-F3", "Find previous", self.find_previous)
933 self.makemenu(self.m_refresh, "&Refresh once \tF5", "Refresh once", self.refresh_once)
937 self.makemenu(self.m_refresh, "&Refresh once \tF5", "Refresh once", self.refresh_once)
934 self.makemenu(self.m_refresh, "Refresh every &1s", "Refresh every second", self.refresh_every_second)
938 self.makemenu(self.m_refresh, "Refresh every &1s", "Refresh every second", self.refresh_every_second)
935 self.makemenu(self.m_refresh, "Refresh every &X seconds", "Refresh every X seconds", self.refresh_interval)
939 self.makemenu(self.m_refresh, "Refresh every &X seconds", "Refresh every X seconds", self.refresh_interval)
936 self.makemenu(self.m_refresh, "&Stop all refresh timers", "Stop refresh timers", self.stop_refresh)
940 self.makemenu(self.m_refresh, "&Stop all refresh timers", "Stop refresh timers", self.stop_refresh)
937 self.menubar.Append(self.m_search, "&Find")
941 self.menubar.Append(self.m_search, "&Find")
938 self.menubar.Append(self.m_sort, "&Sort")
942 self.menubar.Append(self.m_sort, "&Sort")
939 self.menubar.Append(self.m_refresh, "&Refresh")
943 self.menubar.Append(self.m_refresh, "&Refresh")
940 self.menubar.Append(self.m_help, "&Help")
944 self.menubar.Append(self.m_help, "&Help")
941 self.SetMenuBar(self.menubar)
945 self.SetMenuBar(self.menubar)
942 self.searchtext = ""
946 self.searchtext = ""
943 self.searchexpression = ""
947 self.searchexpression = ""
944 self.helpdialog = None
948 self.helpdialog = None
945 self.refresh_interval = 1000
949 self.refresh_interval = 1000
946 self.SetStatusText("Refreshing inactive", 1)
950 self.SetStatusText("Refreshing inactive", 1)
947
951
948 def refresh_once(self, event):
952 def refresh_once(self, event):
949 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
953 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
950 table.refresh_content(event)
954 table.refresh_content(event)
951
955
952 def refresh_interval(self, event):
956 def refresh_interval(self, event):
953 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
957 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
954 dlg = wx.TextEntryDialog(self, "Enter refresh interval (milliseconds):", "Refresh timer:", defaultValue=str(self.refresh_interval))
958 dlg = wx.TextEntryDialog(self, "Enter refresh interval (milliseconds):", "Refresh timer:", defaultValue=str(self.refresh_interval))
955 if dlg.ShowModal() == wx.ID_OK:
959 if dlg.ShowModal() == wx.ID_OK:
956 try:
960 try:
957 milliseconds = int(dlg.GetValue())
961 milliseconds = int(dlg.GetValue())
958 except ValueError, exc:
962 except ValueError, exc:
959 self.SetStatusText(str(exc))
963 self.SetStatusText(str(exc))
960 else:
964 else:
961 table.timer.Start(milliseconds=milliseconds, oneShot=False)
965 table.timer.Start(milliseconds=milliseconds, oneShot=False)
962 self.SetStatusText("Refresh timer set to %s ms" % milliseconds)
966 self.SetStatusText("Refresh timer set to %s ms" % milliseconds)
963 self.SetStatusText("Refresh interval: %s ms" % milliseconds, 1)
967 self.SetStatusText("Refresh interval: %s ms" % milliseconds, 1)
964 self.refresh_interval = milliseconds
968 self.refresh_interval = milliseconds
965 dlg.Destroy()
969 dlg.Destroy()
966
970
967 def stop_refresh(self, event):
971 def stop_refresh(self, event):
968 for i in xrange(self.notebook.GetPageCount()):
972 for i in xrange(self.notebook.GetPageCount()):
969 nb = self.notebook.GetPage(i)
973 nb = self.notebook.GetPage(i)
970 nb.grid.table.timer.Stop()
974 nb.grid.table.timer.Stop()
971 self.SetStatusText("Refreshing inactive", 1)
975 self.SetStatusText("Refreshing inactive", 1)
972
976
973 def refresh_every_second(self, event):
977 def refresh_every_second(self, event):
974 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
978 table = self.notebook.GetPage(self.notebook.GetSelection()).grid.table
975 table.timer.Start(milliseconds=1000, oneShot=False)
979 table.timer.Start(milliseconds=1000, oneShot=False)
976 self.SetStatusText("Refresh interval: 1000 ms", 1)
980 self.SetStatusText("Refresh interval: 1000 ms", 1)
977
981
978 def sortasc(self, event):
982 def sortasc(self, event):
979 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
983 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
980 grid.sortattrasc()
984 grid.sortattrasc()
981
985
982 def sortdesc(self, event):
986 def sortdesc(self, event):
983 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
987 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
984 grid.sortattrdesc()
988 grid.sortattrdesc()
985
989
986 def find_previous(self, event):
990 def find_previous(self, event):
987 """
991 """
988 find previous occurrences
992 find previous occurrences
989 """
993 """
990 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
994 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
991 if self.searchtext:
995 if self.searchtext:
992 row = grid.GetGridCursorRow()
996 row = grid.GetGridCursorRow()
993 col = grid.GetGridCursorCol()
997 col = grid.GetGridCursorCol()
994 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
998 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
995 if col-1 >= 0:
999 if col-1 >= 0:
996 grid.search(self.searchtext, row, col-1, False)
1000 grid.search(self.searchtext, row, col-1, False)
997 else:
1001 else:
998 grid.search(self.searchtext, row-1, grid.table.GetNumberCols()-1, False)
1002 grid.search(self.searchtext, row-1, grid.table.GetNumberCols()-1, False)
999 elif self.searchexpression:
1003 elif self.searchexpression:
1000 self.SetStatusText("Search mode: expression; looking for %s" % repr(self.searchexpression)[2:-1])
1004 self.SetStatusText("Search mode: expression; looking for %s" % repr(self.searchexpression)[2:-1])
1001 grid.searchexpression(searchexp=self.searchexpression, search_forward=False)
1005 grid.searchexpression(searchexp=self.searchexpression, search_forward=False)
1002 else:
1006 else:
1003 self.SetStatusText("No search yet: please enter search-text or -expression")
1007 self.SetStatusText("No search yet: please enter search-text or -expression")
1004
1008
1005 def find_next(self, event):
1009 def find_next(self, event):
1006 """
1010 """
1007 find the next occurrence
1011 find the next occurrence
1008 """
1012 """
1009 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
1013 grid = self.notebook.GetPage(self.notebook.GetSelection()).grid
1010 if self.searchtext != "":
1014 if self.searchtext != "":
1011 row = grid.GetGridCursorRow()
1015 row = grid.GetGridCursorRow()
1012 col = grid.GetGridCursorCol()
1016 col = grid.GetGridCursorCol()
1013 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
1017 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
1014 if col+1 < grid.table.GetNumberCols():
1018 if col+1 < grid.table.GetNumberCols():
1015 grid.search(self.searchtext, row, col+1)
1019 grid.search(self.searchtext, row, col+1)
1016 else:
1020 else:
1017 grid.search(self.searchtext, row+1, 0)
1021 grid.search(self.searchtext, row+1, 0)
1018 elif self.searchexpression != "":
1022 elif self.searchexpression != "":
1019 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1023 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1020 grid.searchexpression(searchexp=self.searchexpression)
1024 grid.searchexpression(searchexp=self.searchexpression)
1021 else:
1025 else:
1022 self.SetStatusText("No search yet: please enter search-text or -expression")
1026 self.SetStatusText("No search yet: please enter search-text or -expression")
1023
1027
1024 def display_help(self, event):
1028 def display_help(self, event):
1025 """
1029 """
1026 Display a help dialog
1030 Display a help dialog
1027 """
1031 """
1028 if self.helpdialog:
1032 if self.helpdialog:
1029 self.helpdialog.Destroy()
1033 self.helpdialog.Destroy()
1030 self.helpdialog = IGridHTMLHelp(None, title="Help", size=wx.Size(600,400))
1034 self.helpdialog = IGridHTMLHelp(None, title="Help", size=wx.Size(600,400))
1031 self.helpdialog.Show()
1035 self.helpdialog.Show()
1032
1036
1033 def display_help_in_browser(self, event):
1037 def display_help_in_browser(self, event):
1034 """
1038 """
1035 Show the help-HTML in a browser (as a ``HtmlWindow`` does not understand
1039 Show the help-HTML in a browser (as a ``HtmlWindow`` does not understand
1036 CSS this looks better)
1040 CSS this looks better)
1037 """
1041 """
1038 filename = urllib.pathname2url(os.path.abspath(os.path.join(os.path.dirname(__file__), "igrid_help.html")))
1042 filename = urllib.pathname2url(os.path.abspath(os.path.join(os.path.dirname(__file__), "igrid_help.html")))
1039 if not filename.startswith("file"):
1043 if not filename.startswith("file"):
1040 filename = "file:" + filename
1044 filename = "file:" + filename
1041 webbrowser.open(filename, new=1, autoraise=True)
1045 webbrowser.open(filename, new=1, autoraise=True)
1042
1046
1043 def enter_searchexpression(self, event):
1047 def enter_searchexpression(self, event):
1044 dlg = wx.TextEntryDialog(self, "Find:", "Find matching expression:", defaultValue=self.searchexpression)
1048 dlg = wx.TextEntryDialog(self, "Find:", "Find matching expression:", defaultValue=self.searchexpression)
1045 if dlg.ShowModal() == wx.ID_OK:
1049 if dlg.ShowModal() == wx.ID_OK:
1046 self.searchexpression = dlg.GetValue()
1050 self.searchexpression = dlg.GetValue()
1047 self.searchtext = ""
1051 self.searchtext = ""
1048 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1052 self.SetStatusText('Search mode: expression; looking for %s' % repr(self.searchexpression)[2:-1])
1049 self.notebook.GetPage(self.notebook.GetSelection()).grid.searchexpression(self.searchexpression)
1053 self.notebook.GetPage(self.notebook.GetSelection()).grid.searchexpression(self.searchexpression)
1050 dlg.Destroy()
1054 dlg.Destroy()
1051
1055
1052 def makemenu(self, menu, label, help, cmd):
1056 def makemenu(self, menu, label, help, cmd):
1053 menu.Append(self.menucounter, label, help)
1057 menu.Append(self.menucounter, label, help)
1054 self.Bind(wx.EVT_MENU, cmd, id=self.menucounter)
1058 self.Bind(wx.EVT_MENU, cmd, id=self.menucounter)
1055 self.menucounter += 1
1059 self.menucounter += 1
1056
1060
1057 def _add_notebook(self, input, *attrs):
1061 def _add_notebook(self, input, *attrs):
1058 # Adds another notebook which has the starting object ``input``
1062 # Adds another notebook which has the starting object ``input``
1059 panel = IGridPanel(self.notebook, input, *attrs)
1063 panel = IGridPanel(self.notebook, input, *attrs)
1060 text = str(ipipe.xformat(input, "header", self.maxtitlelen)[2])
1064 text = str(ipipe.xformat(input, "header", self.maxtitlelen)[2])
1061 if len(text) >= self.maxtitlelen:
1065 if len(text) >= self.maxtitlelen:
1062 text = text[:self.maxtitlelen].rstrip(".") + "..."
1066 text = text[:self.maxtitlelen].rstrip(".") + "..."
1063 self.notebook.AddPage(panel, text, True)
1067 self.notebook.AddPage(panel, text, True)
1064 panel.grid.SetFocus()
1068 panel.grid.SetFocus()
1065 self.Layout()
1069 self.Layout()
1066
1070
1067 def OnCloseWindow(self, event):
1071 def OnCloseWindow(self, event):
1068 self.Destroy()
1072 self.Destroy()
1069
1073
1070 def enter_searchtext(self, event):
1074 def enter_searchtext(self, event):
1071 # Displays a dialog asking for the searchtext
1075 # Displays a dialog asking for the searchtext
1072 dlg = wx.TextEntryDialog(self, "Find:", "Find in list", defaultValue=self.searchtext)
1076 dlg = wx.TextEntryDialog(self, "Find:", "Find in list", defaultValue=self.searchtext)
1073 if dlg.ShowModal() == wx.ID_OK:
1077 if dlg.ShowModal() == wx.ID_OK:
1074 self.searchtext = dlg.GetValue()
1078 self.searchtext = dlg.GetValue()
1075 self.searchexpression = ""
1079 self.searchexpression = ""
1076 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
1080 self.SetStatusText('Search mode: text; looking for %s' % self.searchtext)
1077 self.notebook.GetPage(self.notebook.GetSelection()).grid.search(self.searchtext)
1081 self.notebook.GetPage(self.notebook.GetSelection()).grid.search(self.searchtext)
1078 dlg.Destroy()
1082 dlg.Destroy()
1079
1083
1080
1084
1081 class App(wx.App):
1085 class App(wx.App):
1082 def __init__(self, input):
1086 def __init__(self, input):
1083 self.input = input
1087 self.input = input
1084 self.result = None # Result to be returned to IPython. Set by quit().
1088 self.result = None # Result to be returned to IPython. Set by quit().
1085 wx.App.__init__(self)
1089 wx.App.__init__(self)
1086
1090
1087 def OnInit(self):
1091 def OnInit(self):
1088 frame = IGridFrame(self, self.input)
1092 frame = IGridFrame(self, self.input)
1089 frame.Show()
1093 frame.Show()
1090 self.SetTopWindow(frame)
1094 self.SetTopWindow(frame)
1091 frame.Raise()
1095 frame.Raise()
1092 return True
1096 return True
1093
1097
1094
1098
1095 class igrid(ipipe.Display):
1099 class igrid(ipipe.Display):
1096 """
1100 """
1097 This is a wx-based display object that can be used instead of ``ibrowse``
1101 This is a wx-based display object that can be used instead of ``ibrowse``
1098 (which is curses-based) or ``idump`` (which simply does a print).
1102 (which is curses-based) or ``idump`` (which simply does a print).
1099 """
1103 """
1100 if wx.VERSION < (2, 7):
1104 if wx.VERSION < (2, 7):
1101 def display(self):
1105 def display(self):
1102 try:
1106 try:
1103 # Try to create a "standalone" from. If this works we're probably
1107 # Try to create a "standalone" from. If this works we're probably
1104 # running with -wthread.
1108 # running with -wthread.
1105 # Note that this sets the parent of the frame to None, but we can't
1109 # Note that this sets the parent of the frame to None, but we can't
1106 # pass a result object back to the shell anyway.
1110 # pass a result object back to the shell anyway.
1107 frame = IGridFrame(None, self.input)
1111 frame = IGridFrame(None, self.input)
1108 frame.Show()
1112 frame.Show()
1109 frame.Raise()
1113 frame.Raise()
1110 except wx.PyNoAppError:
1114 except wx.PyNoAppError:
1111 # There's no wx application yet => create one.
1115 # There's no wx application yet => create one.
1112 app = App(self.input)
1116 app = App(self.input)
1113 app.MainLoop()
1117 app.MainLoop()
1114 return app.result
1118 return app.result
1115 else:
1119 else:
1116 # With wx 2.7 it gets simpler.
1120 # With wx 2.7 it gets simpler.
1117 def display(self):
1121 def display(self):
1118 app = App(self.input)
1122 app = App(self.input)
1119 app.MainLoop()
1123 app.MainLoop()
1120 return app.result
1124 return app.result
1121
1125
@@ -1,352 +1,357 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 """ Implementations for various useful completers
3 """ Implementations for various useful completers
4
4
5 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
5 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
6 but the basic idea is to do:
6 but the basic idea is to do:
7
7
8 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
8 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
9
9
10 """
10 """
11 import IPython.ipapi
11 import IPython.ipapi
12 import glob,os,shlex,sys
12 import glob,os,shlex,sys
13 import inspect
13 import inspect
14 from time import time
14 from time import time
15 ip = IPython.ipapi.get()
15 ip = IPython.ipapi.get()
16
16
17 try:
18 set
19 except:
20 from sets import Set as set
21
17 TIMEOUT_STORAGE = 3 #Time in seconds after which the rootmodules will be stored
22 TIMEOUT_STORAGE = 3 #Time in seconds after which the rootmodules will be stored
18 TIMEOUT_GIVEUP = 20 #Time in seconds after which we give up
23 TIMEOUT_GIVEUP = 20 #Time in seconds after which we give up
19
24
20 def quick_completer(cmd, completions):
25 def quick_completer(cmd, completions):
21 """ Easily create a trivial completer for a command.
26 """ Easily create a trivial completer for a command.
22
27
23 Takes either a list of completions, or all completions in string
28 Takes either a list of completions, or all completions in string
24 (that will be split on whitespace)
29 (that will be split on whitespace)
25
30
26 Example::
31 Example::
27
32
28 [d:\ipython]|1> import ipy_completers
33 [d:\ipython]|1> import ipy_completers
29 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
34 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
30 [d:\ipython]|3> foo b<TAB>
35 [d:\ipython]|3> foo b<TAB>
31 bar baz
36 bar baz
32 [d:\ipython]|3> foo ba
37 [d:\ipython]|3> foo ba
33 """
38 """
34 if isinstance(completions, basestring):
39 if isinstance(completions, basestring):
35
40
36 completions = completions.split()
41 completions = completions.split()
37 def do_complete(self,event):
42 def do_complete(self,event):
38 return completions
43 return completions
39
44
40 ip.set_hook('complete_command',do_complete, str_key = cmd)
45 ip.set_hook('complete_command',do_complete, str_key = cmd)
41
46
42 def getRootModules():
47 def getRootModules():
43 """
48 """
44 Returns a list containing the names of all the modules available in the
49 Returns a list containing the names of all the modules available in the
45 folders of the pythonpath.
50 folders of the pythonpath.
46 """
51 """
47 modules = []
52 modules = []
48 if ip.db.has_key('rootmodules'):
53 if ip.db.has_key('rootmodules'):
49 return ip.db['rootmodules']
54 return ip.db['rootmodules']
50 t = time()
55 t = time()
51 store = False
56 store = False
52 for path in sys.path:
57 for path in sys.path:
53 modules += moduleList(path)
58 modules += moduleList(path)
54 if time() - t >= TIMEOUT_STORAGE and not store:
59 if time() - t >= TIMEOUT_STORAGE and not store:
55 store = True
60 store = True
56 print "\nCaching the list of root modules, please wait!"
61 print "\nCaching the list of root modules, please wait!"
57 print "(This will only be done once - type '%rehashx' to " + \
62 print "(This will only be done once - type '%rehashx' to " + \
58 "reset cache!)"
63 "reset cache!)"
59 print
64 print
60 if time() - t > TIMEOUT_GIVEUP:
65 if time() - t > TIMEOUT_GIVEUP:
61 print "This is taking too long, we give up."
66 print "This is taking too long, we give up."
62 print
67 print
63 ip.db['rootmodules'] = []
68 ip.db['rootmodules'] = []
64 return []
69 return []
65
70
66 modules += sys.builtin_module_names
71 modules += sys.builtin_module_names
67
72
68 modules = list(set(modules))
73 modules = list(set(modules))
69 if '__init__' in modules:
74 if '__init__' in modules:
70 modules.remove('__init__')
75 modules.remove('__init__')
71 modules = list(set(modules))
76 modules = list(set(modules))
72 if store:
77 if store:
73 ip.db['rootmodules'] = modules
78 ip.db['rootmodules'] = modules
74 return modules
79 return modules
75
80
76 def moduleList(path):
81 def moduleList(path):
77 """
82 """
78 Return the list containing the names of the modules available in the given
83 Return the list containing the names of the modules available in the given
79 folder.
84 folder.
80 """
85 """
81
86
82 if os.path.isdir(path):
87 if os.path.isdir(path):
83 folder_list = os.listdir(path)
88 folder_list = os.listdir(path)
84 else:
89 else:
85 folder_list = []
90 folder_list = []
86 #folder_list = glob.glob(os.path.join(path,'*'))
91 #folder_list = glob.glob(os.path.join(path,'*'))
87 folder_list = [p for p in folder_list \
92 folder_list = [p for p in folder_list \
88 if os.path.exists(os.path.join(path, p,'__init__.py'))\
93 if os.path.exists(os.path.join(path, p,'__init__.py'))\
89 or p[-3:] in ('.py','.so')\
94 or p[-3:] in ('.py','.so')\
90 or p[-4:] in ('.pyc','.pyo')]
95 or p[-4:] in ('.pyc','.pyo')]
91
96
92 folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
97 folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
93 return folder_list
98 return folder_list
94
99
95 def moduleCompletion(line):
100 def moduleCompletion(line):
96 """
101 """
97 Returns a list containing the completion possibilities for an import line.
102 Returns a list containing the completion possibilities for an import line.
98 The line looks like this :
103 The line looks like this :
99 'import xml.d'
104 'import xml.d'
100 'from xml.dom import'
105 'from xml.dom import'
101 """
106 """
102 def tryImport(mod, only_modules=False):
107 def tryImport(mod, only_modules=False):
103 def isImportable(module, attr):
108 def isImportable(module, attr):
104 if only_modules:
109 if only_modules:
105 return inspect.ismodule(getattr(module, attr))
110 return inspect.ismodule(getattr(module, attr))
106 else:
111 else:
107 return not(attr[:2] == '__' and attr[-2:] == '__')
112 return not(attr[:2] == '__' and attr[-2:] == '__')
108 try:
113 try:
109 m = __import__(mod)
114 m = __import__(mod)
110 except:
115 except:
111 return []
116 return []
112 mods = mod.split('.')
117 mods = mod.split('.')
113 for module in mods[1:]:
118 for module in mods[1:]:
114 m = getattr(m,module)
119 m = getattr(m,module)
115 if (not hasattr(m, '__file__')) or (not only_modules) or\
120 if (not hasattr(m, '__file__')) or (not only_modules) or\
116 (hasattr(m, '__file__') and '__init__' in m.__file__):
121 (hasattr(m, '__file__') and '__init__' in m.__file__):
117 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
122 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
118 completion_list.extend(getattr(m,'__all__',[]))
123 completion_list.extend(getattr(m,'__all__',[]))
119 if hasattr(m, '__file__') and '__init__' in m.__file__:
124 if hasattr(m, '__file__') and '__init__' in m.__file__:
120 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
125 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
121 completion_list = list(set(completion_list))
126 completion_list = list(set(completion_list))
122 if '__init__' in completion_list:
127 if '__init__' in completion_list:
123 completion_list.remove('__init__')
128 completion_list.remove('__init__')
124 return completion_list
129 return completion_list
125
130
126 words = line.split(' ')
131 words = line.split(' ')
127 if len(words) == 3 and words[0] == 'from':
132 if len(words) == 3 and words[0] == 'from':
128 return ['import ']
133 return ['import ']
129 if len(words) < 3 and (words[0] in ['import','from']) :
134 if len(words) < 3 and (words[0] in ['import','from']) :
130 if len(words) == 1:
135 if len(words) == 1:
131 return getRootModules()
136 return getRootModules()
132 mod = words[1].split('.')
137 mod = words[1].split('.')
133 if len(mod) < 2:
138 if len(mod) < 2:
134 return getRootModules()
139 return getRootModules()
135 completion_list = tryImport('.'.join(mod[:-1]), True)
140 completion_list = tryImport('.'.join(mod[:-1]), True)
136 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
141 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
137 return completion_list
142 return completion_list
138 if len(words) >= 3 and words[0] == 'from':
143 if len(words) >= 3 and words[0] == 'from':
139 mod = words[1]
144 mod = words[1]
140 return tryImport(mod)
145 return tryImport(mod)
141
146
142 def vcs_completer(commands, event):
147 def vcs_completer(commands, event):
143 """ utility to make writing typical version control app completers easier
148 """ utility to make writing typical version control app completers easier
144
149
145 VCS command line apps typically have the format:
150 VCS command line apps typically have the format:
146
151
147 [sudo ]PROGNAME [help] [command] file file...
152 [sudo ]PROGNAME [help] [command] file file...
148
153
149 """
154 """
150
155
151
156
152 cmd_param = event.line.split()
157 cmd_param = event.line.split()
153 if event.line.endswith(' '):
158 if event.line.endswith(' '):
154 cmd_param.append('')
159 cmd_param.append('')
155
160
156 if cmd_param[0] == 'sudo':
161 if cmd_param[0] == 'sudo':
157 cmd_param = cmd_param[1:]
162 cmd_param = cmd_param[1:]
158
163
159 if len(cmd_param) == 2 or 'help' in cmd_param:
164 if len(cmd_param) == 2 or 'help' in cmd_param:
160 return commands.split()
165 return commands.split()
161
166
162 return ip.IP.Completer.file_matches(event.symbol)
167 return ip.IP.Completer.file_matches(event.symbol)
163
168
164
169
165 pkg_cache = None
170 pkg_cache = None
166
171
167 def module_completer(self,event):
172 def module_completer(self,event):
168 """ Give completions after user has typed 'import ...' or 'from ...'"""
173 """ Give completions after user has typed 'import ...' or 'from ...'"""
169
174
170 # This works in all versions of python. While 2.5 has
175 # This works in all versions of python. While 2.5 has
171 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
176 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
172 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
177 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
173 # of possibly problematic side effects.
178 # of possibly problematic side effects.
174 # This search the folders in the sys.path for available modules.
179 # This search the folders in the sys.path for available modules.
175
180
176 return moduleCompletion(event.line)
181 return moduleCompletion(event.line)
177
182
178
183
179 svn_commands = """\
184 svn_commands = """\
180 add blame praise annotate ann cat checkout co cleanup commit ci copy
185 add blame praise annotate ann cat checkout co cleanup commit ci copy
181 cp delete del remove rm diff di export help ? h import info list ls
186 cp delete del remove rm diff di export help ? h import info list ls
182 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
187 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
183 pe propget pget pg proplist plist pl propset pset ps resolved revert
188 pe propget pget pg proplist plist pl propset pset ps resolved revert
184 status stat st switch sw unlock update
189 status stat st switch sw unlock update
185 """
190 """
186
191
187 def svn_completer(self,event):
192 def svn_completer(self,event):
188 return vcs_completer(svn_commands, event)
193 return vcs_completer(svn_commands, event)
189
194
190
195
191 hg_commands = """
196 hg_commands = """
192 add addremove annotate archive backout branch branches bundle cat
197 add addremove annotate archive backout branch branches bundle cat
193 clone commit copy diff export grep heads help identify import incoming
198 clone commit copy diff export grep heads help identify import incoming
194 init locate log manifest merge outgoing parents paths pull push
199 init locate log manifest merge outgoing parents paths pull push
195 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
200 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
196 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
201 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
197 qselect qseries qtop qunapplied recover remove rename revert rollback
202 qselect qseries qtop qunapplied recover remove rename revert rollback
198 root serve showconfig status strip tag tags tip unbundle update verify
203 root serve showconfig status strip tag tags tip unbundle update verify
199 version
204 version
200 """
205 """
201
206
202 def hg_completer(self,event):
207 def hg_completer(self,event):
203 """ Completer for mercurial commands """
208 """ Completer for mercurial commands """
204
209
205 return vcs_completer(hg_commands, event)
210 return vcs_completer(hg_commands, event)
206
211
207
212
208
213
209 bzr_commands = """
214 bzr_commands = """
210 add annotate bind branch break-lock bundle-revisions cat check
215 add annotate bind branch break-lock bundle-revisions cat check
211 checkout commit conflicts deleted diff export gannotate gbranch
216 checkout commit conflicts deleted diff export gannotate gbranch
212 gcommit gdiff help ignore ignored info init init-repository inventory
217 gcommit gdiff help ignore ignored info init init-repository inventory
213 log merge missing mkdir mv nick pull push reconcile register-branch
218 log merge missing mkdir mv nick pull push reconcile register-branch
214 remerge remove renames resolve revert revno root serve sign-my-commits
219 remerge remove renames resolve revert revno root serve sign-my-commits
215 status testament unbind uncommit unknowns update upgrade version
220 status testament unbind uncommit unknowns update upgrade version
216 version-info visualise whoami
221 version-info visualise whoami
217 """
222 """
218
223
219 def bzr_completer(self,event):
224 def bzr_completer(self,event):
220 """ Completer for bazaar commands """
225 """ Completer for bazaar commands """
221 cmd_param = event.line.split()
226 cmd_param = event.line.split()
222 if event.line.endswith(' '):
227 if event.line.endswith(' '):
223 cmd_param.append('')
228 cmd_param.append('')
224
229
225 if len(cmd_param) > 2:
230 if len(cmd_param) > 2:
226 cmd = cmd_param[1]
231 cmd = cmd_param[1]
227 param = cmd_param[-1]
232 param = cmd_param[-1]
228 output_file = (param == '--output=')
233 output_file = (param == '--output=')
229 if cmd == 'help':
234 if cmd == 'help':
230 return bzr_commands.split()
235 return bzr_commands.split()
231 elif cmd in ['bundle-revisions','conflicts',
236 elif cmd in ['bundle-revisions','conflicts',
232 'deleted','nick','register-branch',
237 'deleted','nick','register-branch',
233 'serve','unbind','upgrade','version',
238 'serve','unbind','upgrade','version',
234 'whoami'] and not output_file:
239 'whoami'] and not output_file:
235 return []
240 return []
236 else:
241 else:
237 # the rest are probably file names
242 # the rest are probably file names
238 return ip.IP.Completer.file_matches(event.symbol)
243 return ip.IP.Completer.file_matches(event.symbol)
239
244
240 return bzr_commands.split()
245 return bzr_commands.split()
241
246
242
247
243 def shlex_split(x):
248 def shlex_split(x):
244 """Helper function to split lines into segments."""
249 """Helper function to split lines into segments."""
245 #shlex.split raise exception if syntax error in sh syntax
250 #shlex.split raise exception if syntax error in sh syntax
246 #for example if no closing " is found. This function keeps dropping
251 #for example if no closing " is found. This function keeps dropping
247 #the last character of the line until shlex.split does not raise
252 #the last character of the line until shlex.split does not raise
248 #exception. Adds end of the line to the result of shlex.split
253 #exception. Adds end of the line to the result of shlex.split
249 #example: %run "c:/python -> ['%run','"c:/python']
254 #example: %run "c:/python -> ['%run','"c:/python']
250 endofline=[]
255 endofline=[]
251 while x!="":
256 while x!="":
252 try:
257 try:
253 comps=shlex.split(x)
258 comps=shlex.split(x)
254 if len(endofline)>=1:
259 if len(endofline)>=1:
255 comps.append("".join(endofline))
260 comps.append("".join(endofline))
256 return comps
261 return comps
257 except ValueError:
262 except ValueError:
258 endofline=[x[-1:]]+endofline
263 endofline=[x[-1:]]+endofline
259 x=x[:-1]
264 x=x[:-1]
260 return ["".join(endofline)]
265 return ["".join(endofline)]
261
266
262 def runlistpy(self, event):
267 def runlistpy(self, event):
263 comps = shlex_split(event.line)
268 comps = shlex_split(event.line)
264 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
269 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
265
270
266 #print "\nev=",event # dbg
271 #print "\nev=",event # dbg
267 #print "rp=",relpath # dbg
272 #print "rp=",relpath # dbg
268 #print 'comps=',comps # dbg
273 #print 'comps=',comps # dbg
269
274
270 lglob = glob.glob
275 lglob = glob.glob
271 isdir = os.path.isdir
276 isdir = os.path.isdir
272 if relpath.startswith('~'):
277 if relpath.startswith('~'):
273 relpath = os.path.expanduser(relpath)
278 relpath = os.path.expanduser(relpath)
274 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
279 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
275 if isdir(f)]
280 if isdir(f)]
276
281
277 # Find if the user has already typed the first filename, after which we
282 # Find if the user has already typed the first filename, after which we
278 # should complete on all files, since after the first one other files may
283 # should complete on all files, since after the first one other files may
279 # be arguments to the input script.
284 # be arguments to the input script.
280 #filter(
285 #filter(
281 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy'),comps):
286 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy'),comps):
282 pys = [f.replace('\\','/') for f in lglob('*')]
287 pys = [f.replace('\\','/') for f in lglob('*')]
283 else:
288 else:
284 pys = [f.replace('\\','/')
289 pys = [f.replace('\\','/')
285 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy')]
290 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy')]
286 return dirs + pys
291 return dirs + pys
287
292
288
293
289 def cd_completer(self, event):
294 def cd_completer(self, event):
290 relpath = event.symbol
295 relpath = event.symbol
291 #print event # dbg
296 #print event # dbg
292 if '-b' in event.line:
297 if '-b' in event.line:
293 # return only bookmark completions
298 # return only bookmark completions
294 bkms = self.db.get('bookmarks',{})
299 bkms = self.db.get('bookmarks',{})
295 return bkms.keys()
300 return bkms.keys()
296
301
297
302
298 if event.symbol == '-':
303 if event.symbol == '-':
299 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
304 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
300 # jump in directory history by number
305 # jump in directory history by number
301 fmt = '-%0' + width_dh +'d [%s]'
306 fmt = '-%0' + width_dh +'d [%s]'
302 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
307 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
303 if len(ents) > 1:
308 if len(ents) > 1:
304 return ents
309 return ents
305 return []
310 return []
306
311
307 if relpath.startswith('~'):
312 if relpath.startswith('~'):
308 relpath = os.path.expanduser(relpath).replace('\\','/')
313 relpath = os.path.expanduser(relpath).replace('\\','/')
309 found = []
314 found = []
310 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
315 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
311 if os.path.isdir(f)]:
316 if os.path.isdir(f)]:
312 if ' ' in d:
317 if ' ' in d:
313 # we don't want to deal with any of that, complex code
318 # we don't want to deal with any of that, complex code
314 # for this is elsewhere
319 # for this is elsewhere
315 raise IPython.ipapi.TryNext
320 raise IPython.ipapi.TryNext
316 found.append( d )
321 found.append( d )
317
322
318 if not found:
323 if not found:
319 if os.path.isdir(relpath):
324 if os.path.isdir(relpath):
320 return [relpath]
325 return [relpath]
321 raise IPython.ipapi.TryNext
326 raise IPython.ipapi.TryNext
322 return found
327 return found
323
328
324 def apt_get_packages(prefix):
329 def apt_get_packages(prefix):
325 out = os.popen('apt-cache pkgnames')
330 out = os.popen('apt-cache pkgnames')
326 for p in out:
331 for p in out:
327 if p.startswith(prefix):
332 if p.startswith(prefix):
328 yield p.rstrip()
333 yield p.rstrip()
329
334
330
335
331 apt_commands = """\
336 apt_commands = """\
332 update upgrade install remove purge source build-dep dist-upgrade
337 update upgrade install remove purge source build-dep dist-upgrade
333 dselect-upgrade clean autoclean check"""
338 dselect-upgrade clean autoclean check"""
334
339
335 def apt_completer(self, event):
340 def apt_completer(self, event):
336 """ Completer for apt-get (uses apt-cache internally)
341 """ Completer for apt-get (uses apt-cache internally)
337
342
338 """
343 """
339
344
340
345
341 cmd_param = event.line.split()
346 cmd_param = event.line.split()
342 if event.line.endswith(' '):
347 if event.line.endswith(' '):
343 cmd_param.append('')
348 cmd_param.append('')
344
349
345 if cmd_param[0] == 'sudo':
350 if cmd_param[0] == 'sudo':
346 cmd_param = cmd_param[1:]
351 cmd_param = cmd_param[1:]
347
352
348 if len(cmd_param) == 2 or 'help' in cmd_param:
353 if len(cmd_param) == 2 or 'help' in cmd_param:
349 return apt_commands.split()
354 return apt_commands.split()
350
355
351 return list(apt_get_packages(event.symbol))
356 return list(apt_get_packages(event.symbol))
352
357
@@ -1,180 +1,184 b''
1 """Traits-aware tab completion.
1 """Traits-aware tab completion.
2
2
3 This module provides a custom tab-completer that intelligently hides the names
3 This module provides a custom tab-completer that intelligently hides the names
4 that the enthought.traits library (http://code.enthought.com/traits)
4 that the enthought.traits library (http://code.enthought.com/traits)
5 automatically adds to all objects that inherit from its base HasTraits class.
5 automatically adds to all objects that inherit from its base HasTraits class.
6
6
7
7
8 Activation
8 Activation
9 ==========
9 ==========
10
10
11 To use this, put in your ~/.ipython/ipy_user_conf.py file:
11 To use this, put in your ~/.ipython/ipy_user_conf.py file:
12
12
13 from ipy_traits_completer import activate
13 from ipy_traits_completer import activate
14 activate([complete_threshold])
14 activate([complete_threshold])
15
15
16 The optional complete_threshold argument is the minimal length of text you need
16 The optional complete_threshold argument is the minimal length of text you need
17 to type for tab-completion to list names that are automatically generated by
17 to type for tab-completion to list names that are automatically generated by
18 traits. The default value is 3. Note that at runtime, you can change this
18 traits. The default value is 3. Note that at runtime, you can change this
19 value simply by doing:
19 value simply by doing:
20
20
21 import ipy_traits_completer
21 import ipy_traits_completer
22 ipy_traits_completer.COMPLETE_THRESHOLD = 4
22 ipy_traits_completer.COMPLETE_THRESHOLD = 4
23
23
24
24
25 Usage
25 Usage
26 =====
26 =====
27
27
28 The system works as follows. If t is an empty object that HasTraits, then
28 The system works as follows. If t is an empty object that HasTraits, then
29 (assuming the threshold is at the default value of 3):
29 (assuming the threshold is at the default value of 3):
30
30
31 In [7]: t.ed<TAB>
31 In [7]: t.ed<TAB>
32
32
33 doesn't show anything at all, but:
33 doesn't show anything at all, but:
34
34
35 In [7]: t.edi<TAB>
35 In [7]: t.edi<TAB>
36 t.edit_traits t.editable_traits
36 t.edit_traits t.editable_traits
37
37
38 shows these two names that come from traits. This allows you to complete on
38 shows these two names that come from traits. This allows you to complete on
39 the traits-specific names by typing at least 3 letters from them (or whatever
39 the traits-specific names by typing at least 3 letters from them (or whatever
40 you set your threshold to), but to otherwise not see them in normal completion.
40 you set your threshold to), but to otherwise not see them in normal completion.
41
41
42
42
43 Notes
43 Notes
44 =====
44 =====
45
45
46 - This requires Python 2.4 to work (I use sets). I don't think anyone is
46 - This requires Python 2.4 to work (I use sets). I don't think anyone is
47 using traits with 2.3 anyway, so that's OK.
47 using traits with 2.3 anyway, so that's OK.
48 """
48 """
49
49
50 #############################################################################
50 #############################################################################
51 # External imports
51 # External imports
52 from enthought.traits import api as T
52 from enthought.traits import api as T
53
53
54 # IPython imports
54 # IPython imports
55 from IPython.ipapi import TryNext, get as ipget
55 from IPython.ipapi import TryNext, get as ipget
56 from IPython.genutils import dir2
56 from IPython.genutils import dir2
57 try:
58 set
59 except:
60 from sets import Set as set
57
61
58 #############################################################################
62 #############################################################################
59 # Module constants
63 # Module constants
60
64
61 # The completion threshold
65 # The completion threshold
62 # This is currently implemented as a module global, since this sytem isn't
66 # This is currently implemented as a module global, since this sytem isn't
63 # likely to be modified at runtime by multiple instances. If needed in the
67 # likely to be modified at runtime by multiple instances. If needed in the
64 # future, we can always make it local to the completer as a function attribute.
68 # future, we can always make it local to the completer as a function attribute.
65 COMPLETE_THRESHOLD = 3
69 COMPLETE_THRESHOLD = 3
66
70
67 # Set of names that Traits automatically adds to ANY traits-inheriting object.
71 # Set of names that Traits automatically adds to ANY traits-inheriting object.
68 # These are the names we'll filter out.
72 # These are the names we'll filter out.
69 TRAIT_NAMES = set( dir2(T.HasTraits()) ) - set( dir2(object()) )
73 TRAIT_NAMES = set( dir2(T.HasTraits()) ) - set( dir2(object()) )
70
74
71 #############################################################################
75 #############################################################################
72 # Code begins
76 # Code begins
73
77
74 def trait_completer(self,event):
78 def trait_completer(self,event):
75 """A custom IPython tab-completer that is traits-aware.
79 """A custom IPython tab-completer that is traits-aware.
76
80
77 It tries to hide the internal traits attributes, and reveal them only when
81 It tries to hide the internal traits attributes, and reveal them only when
78 it can reasonably guess that the user really is after one of them.
82 it can reasonably guess that the user really is after one of them.
79 """
83 """
80
84
81 #print '\nevent is:',event # dbg
85 #print '\nevent is:',event # dbg
82 symbol_parts = event.symbol.split('.')
86 symbol_parts = event.symbol.split('.')
83 base = '.'.join(symbol_parts[:-1])
87 base = '.'.join(symbol_parts[:-1])
84 #print 'base:',base # dbg
88 #print 'base:',base # dbg
85
89
86 oinfo = self._ofind(base)
90 oinfo = self._ofind(base)
87 if not oinfo['found']:
91 if not oinfo['found']:
88 raise TryNext
92 raise TryNext
89
93
90 obj = oinfo['obj']
94 obj = oinfo['obj']
91 # OK, we got the object. See if it's traits, else punt
95 # OK, we got the object. See if it's traits, else punt
92 if not isinstance(obj,T.HasTraits):
96 if not isinstance(obj,T.HasTraits):
93 raise TryNext
97 raise TryNext
94
98
95 # it's a traits object, don't show the tr* attributes unless the completion
99 # it's a traits object, don't show the tr* attributes unless the completion
96 # begins with 'tr'
100 # begins with 'tr'
97 attrs = dir2(obj)
101 attrs = dir2(obj)
98 # Now, filter out the attributes that start with the user's request
102 # Now, filter out the attributes that start with the user's request
99 attr_start = symbol_parts[-1]
103 attr_start = symbol_parts[-1]
100 if attr_start:
104 if attr_start:
101 attrs = [a for a in attrs if a.startswith(attr_start)]
105 attrs = [a for a in attrs if a.startswith(attr_start)]
102
106
103 # Let's also respect the user's readline_omit__names setting:
107 # Let's also respect the user's readline_omit__names setting:
104 omit__names = ipget().options.readline_omit__names
108 omit__names = ipget().options.readline_omit__names
105 if omit__names == 1:
109 if omit__names == 1:
106 attrs = [a for a in attrs if not a.startswith('__')]
110 attrs = [a for a in attrs if not a.startswith('__')]
107 elif omit__names == 2:
111 elif omit__names == 2:
108 attrs = [a for a in attrs if not a.startswith('_')]
112 attrs = [a for a in attrs if not a.startswith('_')]
109
113
110 #print '\nastart:<%r>' % attr_start # dbg
114 #print '\nastart:<%r>' % attr_start # dbg
111
115
112 if len(attr_start)<COMPLETE_THRESHOLD:
116 if len(attr_start)<COMPLETE_THRESHOLD:
113 attrs = list(set(attrs) - TRAIT_NAMES)
117 attrs = list(set(attrs) - TRAIT_NAMES)
114
118
115 # The base of the completion, so we can form the final results list
119 # The base of the completion, so we can form the final results list
116 bdot = base+'.'
120 bdot = base+'.'
117
121
118 tcomp = [bdot+a for a in attrs]
122 tcomp = [bdot+a for a in attrs]
119 #print 'tcomp:',tcomp
123 #print 'tcomp:',tcomp
120 return tcomp
124 return tcomp
121
125
122 def activate(complete_threshold = COMPLETE_THRESHOLD):
126 def activate(complete_threshold = COMPLETE_THRESHOLD):
123 """Activate the Traits completer.
127 """Activate the Traits completer.
124
128
125 :Keywords:
129 :Keywords:
126 complete_threshold : int
130 complete_threshold : int
127 The minimum number of letters that a user must type in order to
131 The minimum number of letters that a user must type in order to
128 activate completion of traits-private names."""
132 activate completion of traits-private names."""
129
133
130 if not (isinstance(complete_threshold,int) and
134 if not (isinstance(complete_threshold,int) and
131 complete_threshold>0):
135 complete_threshold>0):
132 e='complete_threshold must be a positive integer, not %r' % \
136 e='complete_threshold must be a positive integer, not %r' % \
133 complete_threshold
137 complete_threshold
134 raise ValueError(e)
138 raise ValueError(e)
135
139
136 # Set the module global
140 # Set the module global
137 global COMPLETE_THRESHOLD
141 global COMPLETE_THRESHOLD
138 COMPLETE_THRESHOLD = complete_threshold
142 COMPLETE_THRESHOLD = complete_threshold
139
143
140 # Activate the traits aware completer
144 # Activate the traits aware completer
141 ip = ipget()
145 ip = ipget()
142 ip.set_hook('complete_command', trait_completer, re_key = '.*')
146 ip.set_hook('complete_command', trait_completer, re_key = '.*')
143
147
144
148
145 #############################################################################
149 #############################################################################
146 if __name__ == '__main__':
150 if __name__ == '__main__':
147 # Testing/debugging
151 # Testing/debugging
148
152
149 # A sorted list of the names we'll filter out
153 # A sorted list of the names we'll filter out
150 TNL = list(TRAIT_NAMES)
154 TNL = list(TRAIT_NAMES)
151 TNL.sort()
155 TNL.sort()
152
156
153 # Make a few objects for testing
157 # Make a few objects for testing
154 class TClean(T.HasTraits): pass
158 class TClean(T.HasTraits): pass
155 class Bunch(object): pass
159 class Bunch(object): pass
156 # A clean traits object
160 # A clean traits object
157 t = TClean()
161 t = TClean()
158 # A nested object containing t
162 # A nested object containing t
159 f = Bunch()
163 f = Bunch()
160 f.t = t
164 f.t = t
161 # And a naked new-style object
165 # And a naked new-style object
162 o = object()
166 o = object()
163
167
164 ip = ipget().IP
168 ip = ipget().IP
165
169
166 # A few simplistic tests
170 # A few simplistic tests
167
171
168 # Reset the threshold to the default, in case the test is running inside an
172 # Reset the threshold to the default, in case the test is running inside an
169 # instance of ipython that changed it
173 # instance of ipython that changed it
170 import ipy_traits_completer
174 import ipy_traits_completer
171 ipy_traits_completer.COMPLETE_THRESHOLD = 3
175 ipy_traits_completer.COMPLETE_THRESHOLD = 3
172
176
173 assert ip.complete('t.ed') ==[]
177 assert ip.complete('t.ed') ==[]
174
178
175 # For some bizarre reason, these fail on the first time I run them, but not
179 # For some bizarre reason, these fail on the first time I run them, but not
176 # afterwards. Traits does some really weird stuff at object instantiation
180 # afterwards. Traits does some really weird stuff at object instantiation
177 # time...
181 # time...
178 ta = ip.complete('t.edi')
182 ta = ip.complete('t.edi')
179 assert ta == ['t.edit_traits', 't.editable_traits']
183 assert ta == ['t.edit_traits', 't.editable_traits']
180 print 'Tests OK'
184 print 'Tests OK'
@@ -1,1990 +1,1996 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 General purpose utilities.
3 General purpose utilities.
4
4
5 This is a grab-bag of stuff I find useful in most programs I write. Some of
5 This is a grab-bag of stuff I find useful in most programs I write. Some of
6 these things are also convenient when working at the command line.
6 these things are also convenient when working at the command line.
7
7
8 $Id: genutils.py 2888 2007-12-12 17:20:42Z vivainio $"""
8 $Id: genutils.py 2937 2008-01-16 15:25:59Z vivainio $"""
9
9
10 #*****************************************************************************
10 #*****************************************************************************
11 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
11 # Copyright (C) 2001-2006 Fernando Perez. <fperez@colorado.edu>
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #*****************************************************************************
15 #*****************************************************************************
16
16
17 from IPython import Release
17 from IPython import Release
18 __author__ = '%s <%s>' % Release.authors['Fernando']
18 __author__ = '%s <%s>' % Release.authors['Fernando']
19 __license__ = Release.license
19 __license__ = Release.license
20
20
21 #****************************************************************************
21 #****************************************************************************
22 # required modules from the Python standard library
22 # required modules from the Python standard library
23 import __main__
23 import __main__
24 import commands
24 import commands
25 import doctest
25 import doctest
26 import os
26 import os
27 import re
27 import re
28 import shlex
28 import shlex
29 import shutil
29 import shutil
30 import sys
30 import sys
31 import tempfile
31 import tempfile
32 import time
32 import time
33 import types
33 import types
34 import warnings
34 import warnings
35
35
36 # Other IPython utilities
36 # Other IPython utilities
37 import IPython
37 import IPython
38 from IPython.Itpl import Itpl,itpl,printpl
38 from IPython.Itpl import Itpl,itpl,printpl
39 from IPython import DPyGetOpt, platutils
39 from IPython import DPyGetOpt, platutils
40 from IPython.generics import result_display
40 from IPython.generics import result_display
41 from path import path
41 from path import path
42 if os.name == "nt":
42 if os.name == "nt":
43 from IPython.winconsole import get_console_size
43 from IPython.winconsole import get_console_size
44
44
45 try:
46 set
47 except:
48 from sets import Set as set
49
50
45 #****************************************************************************
51 #****************************************************************************
46 # Exceptions
52 # Exceptions
47 class Error(Exception):
53 class Error(Exception):
48 """Base class for exceptions in this module."""
54 """Base class for exceptions in this module."""
49 pass
55 pass
50
56
51 #----------------------------------------------------------------------------
57 #----------------------------------------------------------------------------
52 class IOStream:
58 class IOStream:
53 def __init__(self,stream,fallback):
59 def __init__(self,stream,fallback):
54 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
60 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
55 stream = fallback
61 stream = fallback
56 self.stream = stream
62 self.stream = stream
57 self._swrite = stream.write
63 self._swrite = stream.write
58 self.flush = stream.flush
64 self.flush = stream.flush
59
65
60 def write(self,data):
66 def write(self,data):
61 try:
67 try:
62 self._swrite(data)
68 self._swrite(data)
63 except:
69 except:
64 try:
70 try:
65 # print handles some unicode issues which may trip a plain
71 # print handles some unicode issues which may trip a plain
66 # write() call. Attempt to emulate write() by using a
72 # write() call. Attempt to emulate write() by using a
67 # trailing comma
73 # trailing comma
68 print >> self.stream, data,
74 print >> self.stream, data,
69 except:
75 except:
70 # if we get here, something is seriously broken.
76 # if we get here, something is seriously broken.
71 print >> sys.stderr, \
77 print >> sys.stderr, \
72 'ERROR - failed to write data to stream:', self.stream
78 'ERROR - failed to write data to stream:', self.stream
73
79
74 def close(self):
80 def close(self):
75 pass
81 pass
76
82
77
83
78 class IOTerm:
84 class IOTerm:
79 """ Term holds the file or file-like objects for handling I/O operations.
85 """ Term holds the file or file-like objects for handling I/O operations.
80
86
81 These are normally just sys.stdin, sys.stdout and sys.stderr but for
87 These are normally just sys.stdin, sys.stdout and sys.stderr but for
82 Windows they can can replaced to allow editing the strings before they are
88 Windows they can can replaced to allow editing the strings before they are
83 displayed."""
89 displayed."""
84
90
85 # In the future, having IPython channel all its I/O operations through
91 # In the future, having IPython channel all its I/O operations through
86 # this class will make it easier to embed it into other environments which
92 # this class will make it easier to embed it into other environments which
87 # are not a normal terminal (such as a GUI-based shell)
93 # are not a normal terminal (such as a GUI-based shell)
88 def __init__(self,cin=None,cout=None,cerr=None):
94 def __init__(self,cin=None,cout=None,cerr=None):
89 self.cin = IOStream(cin,sys.stdin)
95 self.cin = IOStream(cin,sys.stdin)
90 self.cout = IOStream(cout,sys.stdout)
96 self.cout = IOStream(cout,sys.stdout)
91 self.cerr = IOStream(cerr,sys.stderr)
97 self.cerr = IOStream(cerr,sys.stderr)
92
98
93 # Global variable to be used for all I/O
99 # Global variable to be used for all I/O
94 Term = IOTerm()
100 Term = IOTerm()
95
101
96 import IPython.rlineimpl as readline
102 import IPython.rlineimpl as readline
97 # Remake Term to use the readline i/o facilities
103 # Remake Term to use the readline i/o facilities
98 if sys.platform == 'win32' and readline.have_readline:
104 if sys.platform == 'win32' and readline.have_readline:
99
105
100 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
106 Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile)
101
107
102
108
103 #****************************************************************************
109 #****************************************************************************
104 # Generic warning/error printer, used by everything else
110 # Generic warning/error printer, used by everything else
105 def warn(msg,level=2,exit_val=1):
111 def warn(msg,level=2,exit_val=1):
106 """Standard warning printer. Gives formatting consistency.
112 """Standard warning printer. Gives formatting consistency.
107
113
108 Output is sent to Term.cerr (sys.stderr by default).
114 Output is sent to Term.cerr (sys.stderr by default).
109
115
110 Options:
116 Options:
111
117
112 -level(2): allows finer control:
118 -level(2): allows finer control:
113 0 -> Do nothing, dummy function.
119 0 -> Do nothing, dummy function.
114 1 -> Print message.
120 1 -> Print message.
115 2 -> Print 'WARNING:' + message. (Default level).
121 2 -> Print 'WARNING:' + message. (Default level).
116 3 -> Print 'ERROR:' + message.
122 3 -> Print 'ERROR:' + message.
117 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
123 4 -> Print 'FATAL ERROR:' + message and trigger a sys.exit(exit_val).
118
124
119 -exit_val (1): exit value returned by sys.exit() for a level 4
125 -exit_val (1): exit value returned by sys.exit() for a level 4
120 warning. Ignored for all other levels."""
126 warning. Ignored for all other levels."""
121
127
122 if level>0:
128 if level>0:
123 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
129 header = ['','','WARNING: ','ERROR: ','FATAL ERROR: ']
124 print >> Term.cerr, '%s%s' % (header[level],msg)
130 print >> Term.cerr, '%s%s' % (header[level],msg)
125 if level == 4:
131 if level == 4:
126 print >> Term.cerr,'Exiting.\n'
132 print >> Term.cerr,'Exiting.\n'
127 sys.exit(exit_val)
133 sys.exit(exit_val)
128
134
129 def info(msg):
135 def info(msg):
130 """Equivalent to warn(msg,level=1)."""
136 """Equivalent to warn(msg,level=1)."""
131
137
132 warn(msg,level=1)
138 warn(msg,level=1)
133
139
134 def error(msg):
140 def error(msg):
135 """Equivalent to warn(msg,level=3)."""
141 """Equivalent to warn(msg,level=3)."""
136
142
137 warn(msg,level=3)
143 warn(msg,level=3)
138
144
139 def fatal(msg,exit_val=1):
145 def fatal(msg,exit_val=1):
140 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
146 """Equivalent to warn(msg,exit_val=exit_val,level=4)."""
141
147
142 warn(msg,exit_val=exit_val,level=4)
148 warn(msg,exit_val=exit_val,level=4)
143
149
144 #---------------------------------------------------------------------------
150 #---------------------------------------------------------------------------
145 # Debugging routines
151 # Debugging routines
146 #
152 #
147 def debugx(expr,pre_msg=''):
153 def debugx(expr,pre_msg=''):
148 """Print the value of an expression from the caller's frame.
154 """Print the value of an expression from the caller's frame.
149
155
150 Takes an expression, evaluates it in the caller's frame and prints both
156 Takes an expression, evaluates it in the caller's frame and prints both
151 the given expression and the resulting value (as well as a debug mark
157 the given expression and the resulting value (as well as a debug mark
152 indicating the name of the calling function. The input must be of a form
158 indicating the name of the calling function. The input must be of a form
153 suitable for eval().
159 suitable for eval().
154
160
155 An optional message can be passed, which will be prepended to the printed
161 An optional message can be passed, which will be prepended to the printed
156 expr->value pair."""
162 expr->value pair."""
157
163
158 cf = sys._getframe(1)
164 cf = sys._getframe(1)
159 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
165 print '[DBG:%s] %s%s -> %r' % (cf.f_code.co_name,pre_msg,expr,
160 eval(expr,cf.f_globals,cf.f_locals))
166 eval(expr,cf.f_globals,cf.f_locals))
161
167
162 # deactivate it by uncommenting the following line, which makes it a no-op
168 # deactivate it by uncommenting the following line, which makes it a no-op
163 #def debugx(expr,pre_msg=''): pass
169 #def debugx(expr,pre_msg=''): pass
164
170
165 #----------------------------------------------------------------------------
171 #----------------------------------------------------------------------------
166 StringTypes = types.StringTypes
172 StringTypes = types.StringTypes
167
173
168 # Basic timing functionality
174 # Basic timing functionality
169
175
170 # If possible (Unix), use the resource module instead of time.clock()
176 # If possible (Unix), use the resource module instead of time.clock()
171 try:
177 try:
172 import resource
178 import resource
173 def clocku():
179 def clocku():
174 """clocku() -> floating point number
180 """clocku() -> floating point number
175
181
176 Return the *USER* CPU time in seconds since the start of the process.
182 Return the *USER* CPU time in seconds since the start of the process.
177 This is done via a call to resource.getrusage, so it avoids the
183 This is done via a call to resource.getrusage, so it avoids the
178 wraparound problems in time.clock()."""
184 wraparound problems in time.clock()."""
179
185
180 return resource.getrusage(resource.RUSAGE_SELF)[0]
186 return resource.getrusage(resource.RUSAGE_SELF)[0]
181
187
182 def clocks():
188 def clocks():
183 """clocks() -> floating point number
189 """clocks() -> floating point number
184
190
185 Return the *SYSTEM* CPU time in seconds since the start of the process.
191 Return the *SYSTEM* CPU time in seconds since the start of the process.
186 This is done via a call to resource.getrusage, so it avoids the
192 This is done via a call to resource.getrusage, so it avoids the
187 wraparound problems in time.clock()."""
193 wraparound problems in time.clock()."""
188
194
189 return resource.getrusage(resource.RUSAGE_SELF)[1]
195 return resource.getrusage(resource.RUSAGE_SELF)[1]
190
196
191 def clock():
197 def clock():
192 """clock() -> floating point number
198 """clock() -> floating point number
193
199
194 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
200 Return the *TOTAL USER+SYSTEM* CPU time in seconds since the start of
195 the process. This is done via a call to resource.getrusage, so it
201 the process. This is done via a call to resource.getrusage, so it
196 avoids the wraparound problems in time.clock()."""
202 avoids the wraparound problems in time.clock()."""
197
203
198 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
204 u,s = resource.getrusage(resource.RUSAGE_SELF)[:2]
199 return u+s
205 return u+s
200
206
201 def clock2():
207 def clock2():
202 """clock2() -> (t_user,t_system)
208 """clock2() -> (t_user,t_system)
203
209
204 Similar to clock(), but return a tuple of user/system times."""
210 Similar to clock(), but return a tuple of user/system times."""
205 return resource.getrusage(resource.RUSAGE_SELF)[:2]
211 return resource.getrusage(resource.RUSAGE_SELF)[:2]
206
212
207 except ImportError:
213 except ImportError:
208 # There is no distinction of user/system time under windows, so we just use
214 # There is no distinction of user/system time under windows, so we just use
209 # time.clock() for everything...
215 # time.clock() for everything...
210 clocku = clocks = clock = time.clock
216 clocku = clocks = clock = time.clock
211 def clock2():
217 def clock2():
212 """Under windows, system CPU time can't be measured.
218 """Under windows, system CPU time can't be measured.
213
219
214 This just returns clock() and zero."""
220 This just returns clock() and zero."""
215 return time.clock(),0.0
221 return time.clock(),0.0
216
222
217 def timings_out(reps,func,*args,**kw):
223 def timings_out(reps,func,*args,**kw):
218 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
224 """timings_out(reps,func,*args,**kw) -> (t_total,t_per_call,output)
219
225
220 Execute a function reps times, return a tuple with the elapsed total
226 Execute a function reps times, return a tuple with the elapsed total
221 CPU time in seconds, the time per call and the function's output.
227 CPU time in seconds, the time per call and the function's output.
222
228
223 Under Unix, the return value is the sum of user+system time consumed by
229 Under Unix, the return value is the sum of user+system time consumed by
224 the process, computed via the resource module. This prevents problems
230 the process, computed via the resource module. This prevents problems
225 related to the wraparound effect which the time.clock() function has.
231 related to the wraparound effect which the time.clock() function has.
226
232
227 Under Windows the return value is in wall clock seconds. See the
233 Under Windows the return value is in wall clock seconds. See the
228 documentation for the time module for more details."""
234 documentation for the time module for more details."""
229
235
230 reps = int(reps)
236 reps = int(reps)
231 assert reps >=1, 'reps must be >= 1'
237 assert reps >=1, 'reps must be >= 1'
232 if reps==1:
238 if reps==1:
233 start = clock()
239 start = clock()
234 out = func(*args,**kw)
240 out = func(*args,**kw)
235 tot_time = clock()-start
241 tot_time = clock()-start
236 else:
242 else:
237 rng = xrange(reps-1) # the last time is executed separately to store output
243 rng = xrange(reps-1) # the last time is executed separately to store output
238 start = clock()
244 start = clock()
239 for dummy in rng: func(*args,**kw)
245 for dummy in rng: func(*args,**kw)
240 out = func(*args,**kw) # one last time
246 out = func(*args,**kw) # one last time
241 tot_time = clock()-start
247 tot_time = clock()-start
242 av_time = tot_time / reps
248 av_time = tot_time / reps
243 return tot_time,av_time,out
249 return tot_time,av_time,out
244
250
245 def timings(reps,func,*args,**kw):
251 def timings(reps,func,*args,**kw):
246 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
252 """timings(reps,func,*args,**kw) -> (t_total,t_per_call)
247
253
248 Execute a function reps times, return a tuple with the elapsed total CPU
254 Execute a function reps times, return a tuple with the elapsed total CPU
249 time in seconds and the time per call. These are just the first two values
255 time in seconds and the time per call. These are just the first two values
250 in timings_out()."""
256 in timings_out()."""
251
257
252 return timings_out(reps,func,*args,**kw)[0:2]
258 return timings_out(reps,func,*args,**kw)[0:2]
253
259
254 def timing(func,*args,**kw):
260 def timing(func,*args,**kw):
255 """timing(func,*args,**kw) -> t_total
261 """timing(func,*args,**kw) -> t_total
256
262
257 Execute a function once, return the elapsed total CPU time in
263 Execute a function once, return the elapsed total CPU time in
258 seconds. This is just the first value in timings_out()."""
264 seconds. This is just the first value in timings_out()."""
259
265
260 return timings_out(1,func,*args,**kw)[0]
266 return timings_out(1,func,*args,**kw)[0]
261
267
262 #****************************************************************************
268 #****************************************************************************
263 # file and system
269 # file and system
264
270
265 def arg_split(s,posix=False):
271 def arg_split(s,posix=False):
266 """Split a command line's arguments in a shell-like manner.
272 """Split a command line's arguments in a shell-like manner.
267
273
268 This is a modified version of the standard library's shlex.split()
274 This is a modified version of the standard library's shlex.split()
269 function, but with a default of posix=False for splitting, so that quotes
275 function, but with a default of posix=False for splitting, so that quotes
270 in inputs are respected."""
276 in inputs are respected."""
271
277
272 # XXX - there may be unicode-related problems here!!! I'm not sure that
278 # XXX - there may be unicode-related problems here!!! I'm not sure that
273 # shlex is truly unicode-safe, so it might be necessary to do
279 # shlex is truly unicode-safe, so it might be necessary to do
274 #
280 #
275 # s = s.encode(sys.stdin.encoding)
281 # s = s.encode(sys.stdin.encoding)
276 #
282 #
277 # first, to ensure that shlex gets a normal string. Input from anyone who
283 # first, to ensure that shlex gets a normal string. Input from anyone who
278 # knows more about unicode and shlex than I would be good to have here...
284 # knows more about unicode and shlex than I would be good to have here...
279 lex = shlex.shlex(s, posix=posix)
285 lex = shlex.shlex(s, posix=posix)
280 lex.whitespace_split = True
286 lex.whitespace_split = True
281 return list(lex)
287 return list(lex)
282
288
283 def system(cmd,verbose=0,debug=0,header=''):
289 def system(cmd,verbose=0,debug=0,header=''):
284 """Execute a system command, return its exit status.
290 """Execute a system command, return its exit status.
285
291
286 Options:
292 Options:
287
293
288 - verbose (0): print the command to be executed.
294 - verbose (0): print the command to be executed.
289
295
290 - debug (0): only print, do not actually execute.
296 - debug (0): only print, do not actually execute.
291
297
292 - header (''): Header to print on screen prior to the executed command (it
298 - header (''): Header to print on screen prior to the executed command (it
293 is only prepended to the command, no newlines are added).
299 is only prepended to the command, no newlines are added).
294
300
295 Note: a stateful version of this function is available through the
301 Note: a stateful version of this function is available through the
296 SystemExec class."""
302 SystemExec class."""
297
303
298 stat = 0
304 stat = 0
299 if verbose or debug: print header+cmd
305 if verbose or debug: print header+cmd
300 sys.stdout.flush()
306 sys.stdout.flush()
301 if not debug: stat = os.system(cmd)
307 if not debug: stat = os.system(cmd)
302 return stat
308 return stat
303
309
304 def abbrev_cwd():
310 def abbrev_cwd():
305 """ Return abbreviated version of cwd, e.g. d:mydir """
311 """ Return abbreviated version of cwd, e.g. d:mydir """
306 cwd = os.getcwd().replace('\\','/')
312 cwd = os.getcwd().replace('\\','/')
307 drivepart = ''
313 drivepart = ''
308 tail = cwd
314 tail = cwd
309 if sys.platform == 'win32':
315 if sys.platform == 'win32':
310 if len(cwd) < 4:
316 if len(cwd) < 4:
311 return cwd
317 return cwd
312 drivepart,tail = os.path.splitdrive(cwd)
318 drivepart,tail = os.path.splitdrive(cwd)
313
319
314
320
315 parts = tail.split('/')
321 parts = tail.split('/')
316 if len(parts) > 2:
322 if len(parts) > 2:
317 tail = '/'.join(parts[-2:])
323 tail = '/'.join(parts[-2:])
318
324
319 return (drivepart + (
325 return (drivepart + (
320 cwd == '/' and '/' or tail))
326 cwd == '/' and '/' or tail))
321
327
322
328
323 # This function is used by ipython in a lot of places to make system calls.
329 # This function is used by ipython in a lot of places to make system calls.
324 # We need it to be slightly different under win32, due to the vagaries of
330 # We need it to be slightly different under win32, due to the vagaries of
325 # 'network shares'. A win32 override is below.
331 # 'network shares'. A win32 override is below.
326
332
327 def shell(cmd,verbose=0,debug=0,header=''):
333 def shell(cmd,verbose=0,debug=0,header=''):
328 """Execute a command in the system shell, always return None.
334 """Execute a command in the system shell, always return None.
329
335
330 Options:
336 Options:
331
337
332 - verbose (0): print the command to be executed.
338 - verbose (0): print the command to be executed.
333
339
334 - debug (0): only print, do not actually execute.
340 - debug (0): only print, do not actually execute.
335
341
336 - header (''): Header to print on screen prior to the executed command (it
342 - header (''): Header to print on screen prior to the executed command (it
337 is only prepended to the command, no newlines are added).
343 is only prepended to the command, no newlines are added).
338
344
339 Note: this is similar to genutils.system(), but it returns None so it can
345 Note: this is similar to genutils.system(), but it returns None so it can
340 be conveniently used in interactive loops without getting the return value
346 be conveniently used in interactive loops without getting the return value
341 (typically 0) printed many times."""
347 (typically 0) printed many times."""
342
348
343 stat = 0
349 stat = 0
344 if verbose or debug: print header+cmd
350 if verbose or debug: print header+cmd
345 # flush stdout so we don't mangle python's buffering
351 # flush stdout so we don't mangle python's buffering
346 sys.stdout.flush()
352 sys.stdout.flush()
347
353
348 if not debug:
354 if not debug:
349 platutils.set_term_title("IPy " + cmd)
355 platutils.set_term_title("IPy " + cmd)
350 os.system(cmd)
356 os.system(cmd)
351 platutils.set_term_title("IPy " + abbrev_cwd())
357 platutils.set_term_title("IPy " + abbrev_cwd())
352
358
353 # override shell() for win32 to deal with network shares
359 # override shell() for win32 to deal with network shares
354 if os.name in ('nt','dos'):
360 if os.name in ('nt','dos'):
355
361
356 shell_ori = shell
362 shell_ori = shell
357
363
358 def shell(cmd,verbose=0,debug=0,header=''):
364 def shell(cmd,verbose=0,debug=0,header=''):
359 if os.getcwd().startswith(r"\\"):
365 if os.getcwd().startswith(r"\\"):
360 path = os.getcwd()
366 path = os.getcwd()
361 # change to c drive (cannot be on UNC-share when issuing os.system,
367 # change to c drive (cannot be on UNC-share when issuing os.system,
362 # as cmd.exe cannot handle UNC addresses)
368 # as cmd.exe cannot handle UNC addresses)
363 os.chdir("c:")
369 os.chdir("c:")
364 # issue pushd to the UNC-share and then run the command
370 # issue pushd to the UNC-share and then run the command
365 try:
371 try:
366 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
372 shell_ori('"pushd %s&&"'%path+cmd,verbose,debug,header)
367 finally:
373 finally:
368 os.chdir(path)
374 os.chdir(path)
369 else:
375 else:
370 shell_ori(cmd,verbose,debug,header)
376 shell_ori(cmd,verbose,debug,header)
371
377
372 shell.__doc__ = shell_ori.__doc__
378 shell.__doc__ = shell_ori.__doc__
373
379
374 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
380 def getoutput(cmd,verbose=0,debug=0,header='',split=0):
375 """Dummy substitute for perl's backquotes.
381 """Dummy substitute for perl's backquotes.
376
382
377 Executes a command and returns the output.
383 Executes a command and returns the output.
378
384
379 Accepts the same arguments as system(), plus:
385 Accepts the same arguments as system(), plus:
380
386
381 - split(0): if true, the output is returned as a list split on newlines.
387 - split(0): if true, the output is returned as a list split on newlines.
382
388
383 Note: a stateful version of this function is available through the
389 Note: a stateful version of this function is available through the
384 SystemExec class.
390 SystemExec class.
385
391
386 This is pretty much deprecated and rarely used,
392 This is pretty much deprecated and rarely used,
387 genutils.getoutputerror may be what you need.
393 genutils.getoutputerror may be what you need.
388
394
389 """
395 """
390
396
391 if verbose or debug: print header+cmd
397 if verbose or debug: print header+cmd
392 if not debug:
398 if not debug:
393 output = os.popen(cmd).read()
399 output = os.popen(cmd).read()
394 # stipping last \n is here for backwards compat.
400 # stipping last \n is here for backwards compat.
395 if output.endswith('\n'):
401 if output.endswith('\n'):
396 output = output[:-1]
402 output = output[:-1]
397 if split:
403 if split:
398 return output.split('\n')
404 return output.split('\n')
399 else:
405 else:
400 return output
406 return output
401
407
402 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
408 def getoutputerror(cmd,verbose=0,debug=0,header='',split=0):
403 """Return (standard output,standard error) of executing cmd in a shell.
409 """Return (standard output,standard error) of executing cmd in a shell.
404
410
405 Accepts the same arguments as system(), plus:
411 Accepts the same arguments as system(), plus:
406
412
407 - split(0): if true, each of stdout/err is returned as a list split on
413 - split(0): if true, each of stdout/err is returned as a list split on
408 newlines.
414 newlines.
409
415
410 Note: a stateful version of this function is available through the
416 Note: a stateful version of this function is available through the
411 SystemExec class."""
417 SystemExec class."""
412
418
413 if verbose or debug: print header+cmd
419 if verbose or debug: print header+cmd
414 if not cmd:
420 if not cmd:
415 if split:
421 if split:
416 return [],[]
422 return [],[]
417 else:
423 else:
418 return '',''
424 return '',''
419 if not debug:
425 if not debug:
420 pin,pout,perr = os.popen3(cmd)
426 pin,pout,perr = os.popen3(cmd)
421 tout = pout.read().rstrip()
427 tout = pout.read().rstrip()
422 terr = perr.read().rstrip()
428 terr = perr.read().rstrip()
423 pin.close()
429 pin.close()
424 pout.close()
430 pout.close()
425 perr.close()
431 perr.close()
426 if split:
432 if split:
427 return tout.split('\n'),terr.split('\n')
433 return tout.split('\n'),terr.split('\n')
428 else:
434 else:
429 return tout,terr
435 return tout,terr
430
436
431 # for compatibility with older naming conventions
437 # for compatibility with older naming conventions
432 xsys = system
438 xsys = system
433 bq = getoutput
439 bq = getoutput
434
440
435 class SystemExec:
441 class SystemExec:
436 """Access the system and getoutput functions through a stateful interface.
442 """Access the system and getoutput functions through a stateful interface.
437
443
438 Note: here we refer to the system and getoutput functions from this
444 Note: here we refer to the system and getoutput functions from this
439 library, not the ones from the standard python library.
445 library, not the ones from the standard python library.
440
446
441 This class offers the system and getoutput functions as methods, but the
447 This class offers the system and getoutput functions as methods, but the
442 verbose, debug and header parameters can be set for the instance (at
448 verbose, debug and header parameters can be set for the instance (at
443 creation time or later) so that they don't need to be specified on each
449 creation time or later) so that they don't need to be specified on each
444 call.
450 call.
445
451
446 For efficiency reasons, there's no way to override the parameters on a
452 For efficiency reasons, there's no way to override the parameters on a
447 per-call basis other than by setting instance attributes. If you need
453 per-call basis other than by setting instance attributes. If you need
448 local overrides, it's best to directly call system() or getoutput().
454 local overrides, it's best to directly call system() or getoutput().
449
455
450 The following names are provided as alternate options:
456 The following names are provided as alternate options:
451 - xsys: alias to system
457 - xsys: alias to system
452 - bq: alias to getoutput
458 - bq: alias to getoutput
453
459
454 An instance can then be created as:
460 An instance can then be created as:
455 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
461 >>> sysexec = SystemExec(verbose=1,debug=0,header='Calling: ')
456
462
457 And used as:
463 And used as:
458 >>> sysexec.xsys('pwd')
464 >>> sysexec.xsys('pwd')
459 >>> dirlist = sysexec.bq('ls -l')
465 >>> dirlist = sysexec.bq('ls -l')
460 """
466 """
461
467
462 def __init__(self,verbose=0,debug=0,header='',split=0):
468 def __init__(self,verbose=0,debug=0,header='',split=0):
463 """Specify the instance's values for verbose, debug and header."""
469 """Specify the instance's values for verbose, debug and header."""
464 setattr_list(self,'verbose debug header split')
470 setattr_list(self,'verbose debug header split')
465
471
466 def system(self,cmd):
472 def system(self,cmd):
467 """Stateful interface to system(), with the same keyword parameters."""
473 """Stateful interface to system(), with the same keyword parameters."""
468
474
469 system(cmd,self.verbose,self.debug,self.header)
475 system(cmd,self.verbose,self.debug,self.header)
470
476
471 def shell(self,cmd):
477 def shell(self,cmd):
472 """Stateful interface to shell(), with the same keyword parameters."""
478 """Stateful interface to shell(), with the same keyword parameters."""
473
479
474 shell(cmd,self.verbose,self.debug,self.header)
480 shell(cmd,self.verbose,self.debug,self.header)
475
481
476 xsys = system # alias
482 xsys = system # alias
477
483
478 def getoutput(self,cmd):
484 def getoutput(self,cmd):
479 """Stateful interface to getoutput()."""
485 """Stateful interface to getoutput()."""
480
486
481 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
487 return getoutput(cmd,self.verbose,self.debug,self.header,self.split)
482
488
483 def getoutputerror(self,cmd):
489 def getoutputerror(self,cmd):
484 """Stateful interface to getoutputerror()."""
490 """Stateful interface to getoutputerror()."""
485
491
486 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
492 return getoutputerror(cmd,self.verbose,self.debug,self.header,self.split)
487
493
488 bq = getoutput # alias
494 bq = getoutput # alias
489
495
490 #-----------------------------------------------------------------------------
496 #-----------------------------------------------------------------------------
491 def mutex_opts(dict,ex_op):
497 def mutex_opts(dict,ex_op):
492 """Check for presence of mutually exclusive keys in a dict.
498 """Check for presence of mutually exclusive keys in a dict.
493
499
494 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
500 Call: mutex_opts(dict,[[op1a,op1b],[op2a,op2b]...]"""
495 for op1,op2 in ex_op:
501 for op1,op2 in ex_op:
496 if op1 in dict and op2 in dict:
502 if op1 in dict and op2 in dict:
497 raise ValueError,'\n*** ERROR in Arguments *** '\
503 raise ValueError,'\n*** ERROR in Arguments *** '\
498 'Options '+op1+' and '+op2+' are mutually exclusive.'
504 'Options '+op1+' and '+op2+' are mutually exclusive.'
499
505
500 #-----------------------------------------------------------------------------
506 #-----------------------------------------------------------------------------
501 def get_py_filename(name):
507 def get_py_filename(name):
502 """Return a valid python filename in the current directory.
508 """Return a valid python filename in the current directory.
503
509
504 If the given name is not a file, it adds '.py' and searches again.
510 If the given name is not a file, it adds '.py' and searches again.
505 Raises IOError with an informative message if the file isn't found."""
511 Raises IOError with an informative message if the file isn't found."""
506
512
507 name = os.path.expanduser(name)
513 name = os.path.expanduser(name)
508 if not os.path.isfile(name) and not name.endswith('.py'):
514 if not os.path.isfile(name) and not name.endswith('.py'):
509 name += '.py'
515 name += '.py'
510 if os.path.isfile(name):
516 if os.path.isfile(name):
511 return name
517 return name
512 else:
518 else:
513 raise IOError,'File `%s` not found.' % name
519 raise IOError,'File `%s` not found.' % name
514
520
515 #-----------------------------------------------------------------------------
521 #-----------------------------------------------------------------------------
516 def filefind(fname,alt_dirs = None):
522 def filefind(fname,alt_dirs = None):
517 """Return the given filename either in the current directory, if it
523 """Return the given filename either in the current directory, if it
518 exists, or in a specified list of directories.
524 exists, or in a specified list of directories.
519
525
520 ~ expansion is done on all file and directory names.
526 ~ expansion is done on all file and directory names.
521
527
522 Upon an unsuccessful search, raise an IOError exception."""
528 Upon an unsuccessful search, raise an IOError exception."""
523
529
524 if alt_dirs is None:
530 if alt_dirs is None:
525 try:
531 try:
526 alt_dirs = get_home_dir()
532 alt_dirs = get_home_dir()
527 except HomeDirError:
533 except HomeDirError:
528 alt_dirs = os.getcwd()
534 alt_dirs = os.getcwd()
529 search = [fname] + list_strings(alt_dirs)
535 search = [fname] + list_strings(alt_dirs)
530 search = map(os.path.expanduser,search)
536 search = map(os.path.expanduser,search)
531 #print 'search list for',fname,'list:',search # dbg
537 #print 'search list for',fname,'list:',search # dbg
532 fname = search[0]
538 fname = search[0]
533 if os.path.isfile(fname):
539 if os.path.isfile(fname):
534 return fname
540 return fname
535 for direc in search[1:]:
541 for direc in search[1:]:
536 testname = os.path.join(direc,fname)
542 testname = os.path.join(direc,fname)
537 #print 'testname',testname # dbg
543 #print 'testname',testname # dbg
538 if os.path.isfile(testname):
544 if os.path.isfile(testname):
539 return testname
545 return testname
540 raise IOError,'File' + `fname` + \
546 raise IOError,'File' + `fname` + \
541 ' not found in current or supplied directories:' + `alt_dirs`
547 ' not found in current or supplied directories:' + `alt_dirs`
542
548
543 #----------------------------------------------------------------------------
549 #----------------------------------------------------------------------------
544 def file_read(filename):
550 def file_read(filename):
545 """Read a file and close it. Returns the file source."""
551 """Read a file and close it. Returns the file source."""
546 fobj = open(filename,'r');
552 fobj = open(filename,'r');
547 source = fobj.read();
553 source = fobj.read();
548 fobj.close()
554 fobj.close()
549 return source
555 return source
550
556
551 def file_readlines(filename):
557 def file_readlines(filename):
552 """Read a file and close it. Returns the file source using readlines()."""
558 """Read a file and close it. Returns the file source using readlines()."""
553 fobj = open(filename,'r');
559 fobj = open(filename,'r');
554 lines = fobj.readlines();
560 lines = fobj.readlines();
555 fobj.close()
561 fobj.close()
556 return lines
562 return lines
557
563
558 #----------------------------------------------------------------------------
564 #----------------------------------------------------------------------------
559 def target_outdated(target,deps):
565 def target_outdated(target,deps):
560 """Determine whether a target is out of date.
566 """Determine whether a target is out of date.
561
567
562 target_outdated(target,deps) -> 1/0
568 target_outdated(target,deps) -> 1/0
563
569
564 deps: list of filenames which MUST exist.
570 deps: list of filenames which MUST exist.
565 target: single filename which may or may not exist.
571 target: single filename which may or may not exist.
566
572
567 If target doesn't exist or is older than any file listed in deps, return
573 If target doesn't exist or is older than any file listed in deps, return
568 true, otherwise return false.
574 true, otherwise return false.
569 """
575 """
570 try:
576 try:
571 target_time = os.path.getmtime(target)
577 target_time = os.path.getmtime(target)
572 except os.error:
578 except os.error:
573 return 1
579 return 1
574 for dep in deps:
580 for dep in deps:
575 dep_time = os.path.getmtime(dep)
581 dep_time = os.path.getmtime(dep)
576 if dep_time > target_time:
582 if dep_time > target_time:
577 #print "For target",target,"Dep failed:",dep # dbg
583 #print "For target",target,"Dep failed:",dep # dbg
578 #print "times (dep,tar):",dep_time,target_time # dbg
584 #print "times (dep,tar):",dep_time,target_time # dbg
579 return 1
585 return 1
580 return 0
586 return 0
581
587
582 #-----------------------------------------------------------------------------
588 #-----------------------------------------------------------------------------
583 def target_update(target,deps,cmd):
589 def target_update(target,deps,cmd):
584 """Update a target with a given command given a list of dependencies.
590 """Update a target with a given command given a list of dependencies.
585
591
586 target_update(target,deps,cmd) -> runs cmd if target is outdated.
592 target_update(target,deps,cmd) -> runs cmd if target is outdated.
587
593
588 This is just a wrapper around target_outdated() which calls the given
594 This is just a wrapper around target_outdated() which calls the given
589 command if target is outdated."""
595 command if target is outdated."""
590
596
591 if target_outdated(target,deps):
597 if target_outdated(target,deps):
592 xsys(cmd)
598 xsys(cmd)
593
599
594 #----------------------------------------------------------------------------
600 #----------------------------------------------------------------------------
595 def unquote_ends(istr):
601 def unquote_ends(istr):
596 """Remove a single pair of quotes from the endpoints of a string."""
602 """Remove a single pair of quotes from the endpoints of a string."""
597
603
598 if not istr:
604 if not istr:
599 return istr
605 return istr
600 if (istr[0]=="'" and istr[-1]=="'") or \
606 if (istr[0]=="'" and istr[-1]=="'") or \
601 (istr[0]=='"' and istr[-1]=='"'):
607 (istr[0]=='"' and istr[-1]=='"'):
602 return istr[1:-1]
608 return istr[1:-1]
603 else:
609 else:
604 return istr
610 return istr
605
611
606 #----------------------------------------------------------------------------
612 #----------------------------------------------------------------------------
607 def process_cmdline(argv,names=[],defaults={},usage=''):
613 def process_cmdline(argv,names=[],defaults={},usage=''):
608 """ Process command-line options and arguments.
614 """ Process command-line options and arguments.
609
615
610 Arguments:
616 Arguments:
611
617
612 - argv: list of arguments, typically sys.argv.
618 - argv: list of arguments, typically sys.argv.
613
619
614 - names: list of option names. See DPyGetOpt docs for details on options
620 - names: list of option names. See DPyGetOpt docs for details on options
615 syntax.
621 syntax.
616
622
617 - defaults: dict of default values.
623 - defaults: dict of default values.
618
624
619 - usage: optional usage notice to print if a wrong argument is passed.
625 - usage: optional usage notice to print if a wrong argument is passed.
620
626
621 Return a dict of options and a list of free arguments."""
627 Return a dict of options and a list of free arguments."""
622
628
623 getopt = DPyGetOpt.DPyGetOpt()
629 getopt = DPyGetOpt.DPyGetOpt()
624 getopt.setIgnoreCase(0)
630 getopt.setIgnoreCase(0)
625 getopt.parseConfiguration(names)
631 getopt.parseConfiguration(names)
626
632
627 try:
633 try:
628 getopt.processArguments(argv)
634 getopt.processArguments(argv)
629 except DPyGetOpt.ArgumentError, exc:
635 except DPyGetOpt.ArgumentError, exc:
630 print usage
636 print usage
631 warn('"%s"' % exc,level=4)
637 warn('"%s"' % exc,level=4)
632
638
633 defaults.update(getopt.optionValues)
639 defaults.update(getopt.optionValues)
634 args = getopt.freeValues
640 args = getopt.freeValues
635
641
636 return defaults,args
642 return defaults,args
637
643
638 #----------------------------------------------------------------------------
644 #----------------------------------------------------------------------------
639 def optstr2types(ostr):
645 def optstr2types(ostr):
640 """Convert a string of option names to a dict of type mappings.
646 """Convert a string of option names to a dict of type mappings.
641
647
642 optstr2types(str) -> {None:'string_opts',int:'int_opts',float:'float_opts'}
648 optstr2types(str) -> {None:'string_opts',int:'int_opts',float:'float_opts'}
643
649
644 This is used to get the types of all the options in a string formatted
650 This is used to get the types of all the options in a string formatted
645 with the conventions of DPyGetOpt. The 'type' None is used for options
651 with the conventions of DPyGetOpt. The 'type' None is used for options
646 which are strings (they need no further conversion). This function's main
652 which are strings (they need no further conversion). This function's main
647 use is to get a typemap for use with read_dict().
653 use is to get a typemap for use with read_dict().
648 """
654 """
649
655
650 typeconv = {None:'',int:'',float:''}
656 typeconv = {None:'',int:'',float:''}
651 typemap = {'s':None,'i':int,'f':float}
657 typemap = {'s':None,'i':int,'f':float}
652 opt_re = re.compile(r'([\w]*)([^:=]*:?=?)([sif]?)')
658 opt_re = re.compile(r'([\w]*)([^:=]*:?=?)([sif]?)')
653
659
654 for w in ostr.split():
660 for w in ostr.split():
655 oname,alias,otype = opt_re.match(w).groups()
661 oname,alias,otype = opt_re.match(w).groups()
656 if otype == '' or alias == '!': # simple switches are integers too
662 if otype == '' or alias == '!': # simple switches are integers too
657 otype = 'i'
663 otype = 'i'
658 typeconv[typemap[otype]] += oname + ' '
664 typeconv[typemap[otype]] += oname + ' '
659 return typeconv
665 return typeconv
660
666
661 #----------------------------------------------------------------------------
667 #----------------------------------------------------------------------------
662 def read_dict(filename,type_conv=None,**opt):
668 def read_dict(filename,type_conv=None,**opt):
663
669
664 """Read a dictionary of key=value pairs from an input file, optionally
670 """Read a dictionary of key=value pairs from an input file, optionally
665 performing conversions on the resulting values.
671 performing conversions on the resulting values.
666
672
667 read_dict(filename,type_conv,**opt) -> dict
673 read_dict(filename,type_conv,**opt) -> dict
668
674
669 Only one value per line is accepted, the format should be
675 Only one value per line is accepted, the format should be
670 # optional comments are ignored
676 # optional comments are ignored
671 key value\n
677 key value\n
672
678
673 Args:
679 Args:
674
680
675 - type_conv: A dictionary specifying which keys need to be converted to
681 - type_conv: A dictionary specifying which keys need to be converted to
676 which types. By default all keys are read as strings. This dictionary
682 which types. By default all keys are read as strings. This dictionary
677 should have as its keys valid conversion functions for strings
683 should have as its keys valid conversion functions for strings
678 (int,long,float,complex, or your own). The value for each key
684 (int,long,float,complex, or your own). The value for each key
679 (converter) should be a whitespace separated string containing the names
685 (converter) should be a whitespace separated string containing the names
680 of all the entries in the file to be converted using that function. For
686 of all the entries in the file to be converted using that function. For
681 keys to be left alone, use None as the conversion function (only needed
687 keys to be left alone, use None as the conversion function (only needed
682 with purge=1, see below).
688 with purge=1, see below).
683
689
684 - opt: dictionary with extra options as below (default in parens)
690 - opt: dictionary with extra options as below (default in parens)
685
691
686 purge(0): if set to 1, all keys *not* listed in type_conv are purged out
692 purge(0): if set to 1, all keys *not* listed in type_conv are purged out
687 of the dictionary to be returned. If purge is going to be used, the
693 of the dictionary to be returned. If purge is going to be used, the
688 set of keys to be left as strings also has to be explicitly specified
694 set of keys to be left as strings also has to be explicitly specified
689 using the (non-existent) conversion function None.
695 using the (non-existent) conversion function None.
690
696
691 fs(None): field separator. This is the key/value separator to be used
697 fs(None): field separator. This is the key/value separator to be used
692 when parsing the file. The None default means any whitespace [behavior
698 when parsing the file. The None default means any whitespace [behavior
693 of string.split()].
699 of string.split()].
694
700
695 strip(0): if 1, strip string values of leading/trailinig whitespace.
701 strip(0): if 1, strip string values of leading/trailinig whitespace.
696
702
697 warn(1): warning level if requested keys are not found in file.
703 warn(1): warning level if requested keys are not found in file.
698 - 0: silently ignore.
704 - 0: silently ignore.
699 - 1: inform but proceed.
705 - 1: inform but proceed.
700 - 2: raise KeyError exception.
706 - 2: raise KeyError exception.
701
707
702 no_empty(0): if 1, remove keys with whitespace strings as a value.
708 no_empty(0): if 1, remove keys with whitespace strings as a value.
703
709
704 unique([]): list of keys (or space separated string) which can't be
710 unique([]): list of keys (or space separated string) which can't be
705 repeated. If one such key is found in the file, each new instance
711 repeated. If one such key is found in the file, each new instance
706 overwrites the previous one. For keys not listed here, the behavior is
712 overwrites the previous one. For keys not listed here, the behavior is
707 to make a list of all appearances.
713 to make a list of all appearances.
708
714
709 Example:
715 Example:
710 If the input file test.ini has:
716 If the input file test.ini has:
711 i 3
717 i 3
712 x 4.5
718 x 4.5
713 y 5.5
719 y 5.5
714 s hi ho
720 s hi ho
715 Then:
721 Then:
716
722
717 >>> type_conv={int:'i',float:'x',None:'s'}
723 >>> type_conv={int:'i',float:'x',None:'s'}
718 >>> read_dict('test.ini')
724 >>> read_dict('test.ini')
719 {'i': '3', 's': 'hi ho', 'x': '4.5', 'y': '5.5'}
725 {'i': '3', 's': 'hi ho', 'x': '4.5', 'y': '5.5'}
720 >>> read_dict('test.ini',type_conv)
726 >>> read_dict('test.ini',type_conv)
721 {'i': 3, 's': 'hi ho', 'x': 4.5, 'y': '5.5'}
727 {'i': 3, 's': 'hi ho', 'x': 4.5, 'y': '5.5'}
722 >>> read_dict('test.ini',type_conv,purge=1)
728 >>> read_dict('test.ini',type_conv,purge=1)
723 {'i': 3, 's': 'hi ho', 'x': 4.5}
729 {'i': 3, 's': 'hi ho', 'x': 4.5}
724 """
730 """
725
731
726 # starting config
732 # starting config
727 opt.setdefault('purge',0)
733 opt.setdefault('purge',0)
728 opt.setdefault('fs',None) # field sep defaults to any whitespace
734 opt.setdefault('fs',None) # field sep defaults to any whitespace
729 opt.setdefault('strip',0)
735 opt.setdefault('strip',0)
730 opt.setdefault('warn',1)
736 opt.setdefault('warn',1)
731 opt.setdefault('no_empty',0)
737 opt.setdefault('no_empty',0)
732 opt.setdefault('unique','')
738 opt.setdefault('unique','')
733 if type(opt['unique']) in StringTypes:
739 if type(opt['unique']) in StringTypes:
734 unique_keys = qw(opt['unique'])
740 unique_keys = qw(opt['unique'])
735 elif type(opt['unique']) in (types.TupleType,types.ListType):
741 elif type(opt['unique']) in (types.TupleType,types.ListType):
736 unique_keys = opt['unique']
742 unique_keys = opt['unique']
737 else:
743 else:
738 raise ValueError, 'Unique keys must be given as a string, List or Tuple'
744 raise ValueError, 'Unique keys must be given as a string, List or Tuple'
739
745
740 dict = {}
746 dict = {}
741 # first read in table of values as strings
747 # first read in table of values as strings
742 file = open(filename,'r')
748 file = open(filename,'r')
743 for line in file.readlines():
749 for line in file.readlines():
744 line = line.strip()
750 line = line.strip()
745 if len(line) and line[0]=='#': continue
751 if len(line) and line[0]=='#': continue
746 if len(line)>0:
752 if len(line)>0:
747 lsplit = line.split(opt['fs'],1)
753 lsplit = line.split(opt['fs'],1)
748 try:
754 try:
749 key,val = lsplit
755 key,val = lsplit
750 except ValueError:
756 except ValueError:
751 key,val = lsplit[0],''
757 key,val = lsplit[0],''
752 key = key.strip()
758 key = key.strip()
753 if opt['strip']: val = val.strip()
759 if opt['strip']: val = val.strip()
754 if val == "''" or val == '""': val = ''
760 if val == "''" or val == '""': val = ''
755 if opt['no_empty'] and (val=='' or val.isspace()):
761 if opt['no_empty'] and (val=='' or val.isspace()):
756 continue
762 continue
757 # if a key is found more than once in the file, build a list
763 # if a key is found more than once in the file, build a list
758 # unless it's in the 'unique' list. In that case, last found in file
764 # unless it's in the 'unique' list. In that case, last found in file
759 # takes precedence. User beware.
765 # takes precedence. User beware.
760 try:
766 try:
761 if dict[key] and key in unique_keys:
767 if dict[key] and key in unique_keys:
762 dict[key] = val
768 dict[key] = val
763 elif type(dict[key]) is types.ListType:
769 elif type(dict[key]) is types.ListType:
764 dict[key].append(val)
770 dict[key].append(val)
765 else:
771 else:
766 dict[key] = [dict[key],val]
772 dict[key] = [dict[key],val]
767 except KeyError:
773 except KeyError:
768 dict[key] = val
774 dict[key] = val
769 # purge if requested
775 # purge if requested
770 if opt['purge']:
776 if opt['purge']:
771 accepted_keys = qwflat(type_conv.values())
777 accepted_keys = qwflat(type_conv.values())
772 for key in dict.keys():
778 for key in dict.keys():
773 if key in accepted_keys: continue
779 if key in accepted_keys: continue
774 del(dict[key])
780 del(dict[key])
775 # now convert if requested
781 # now convert if requested
776 if type_conv==None: return dict
782 if type_conv==None: return dict
777 conversions = type_conv.keys()
783 conversions = type_conv.keys()
778 try: conversions.remove(None)
784 try: conversions.remove(None)
779 except: pass
785 except: pass
780 for convert in conversions:
786 for convert in conversions:
781 for val in qw(type_conv[convert]):
787 for val in qw(type_conv[convert]):
782 try:
788 try:
783 dict[val] = convert(dict[val])
789 dict[val] = convert(dict[val])
784 except KeyError,e:
790 except KeyError,e:
785 if opt['warn'] == 0:
791 if opt['warn'] == 0:
786 pass
792 pass
787 elif opt['warn'] == 1:
793 elif opt['warn'] == 1:
788 print >>sys.stderr, 'Warning: key',val,\
794 print >>sys.stderr, 'Warning: key',val,\
789 'not found in file',filename
795 'not found in file',filename
790 elif opt['warn'] == 2:
796 elif opt['warn'] == 2:
791 raise KeyError,e
797 raise KeyError,e
792 else:
798 else:
793 raise ValueError,'Warning level must be 0,1 or 2'
799 raise ValueError,'Warning level must be 0,1 or 2'
794
800
795 return dict
801 return dict
796
802
797 #----------------------------------------------------------------------------
803 #----------------------------------------------------------------------------
798 def flag_calls(func):
804 def flag_calls(func):
799 """Wrap a function to detect and flag when it gets called.
805 """Wrap a function to detect and flag when it gets called.
800
806
801 This is a decorator which takes a function and wraps it in a function with
807 This is a decorator which takes a function and wraps it in a function with
802 a 'called' attribute. wrapper.called is initialized to False.
808 a 'called' attribute. wrapper.called is initialized to False.
803
809
804 The wrapper.called attribute is set to False right before each call to the
810 The wrapper.called attribute is set to False right before each call to the
805 wrapped function, so if the call fails it remains False. After the call
811 wrapped function, so if the call fails it remains False. After the call
806 completes, wrapper.called is set to True and the output is returned.
812 completes, wrapper.called is set to True and the output is returned.
807
813
808 Testing for truth in wrapper.called allows you to determine if a call to
814 Testing for truth in wrapper.called allows you to determine if a call to
809 func() was attempted and succeeded."""
815 func() was attempted and succeeded."""
810
816
811 def wrapper(*args,**kw):
817 def wrapper(*args,**kw):
812 wrapper.called = False
818 wrapper.called = False
813 out = func(*args,**kw)
819 out = func(*args,**kw)
814 wrapper.called = True
820 wrapper.called = True
815 return out
821 return out
816
822
817 wrapper.called = False
823 wrapper.called = False
818 wrapper.__doc__ = func.__doc__
824 wrapper.__doc__ = func.__doc__
819 return wrapper
825 return wrapper
820
826
821 #----------------------------------------------------------------------------
827 #----------------------------------------------------------------------------
822 def dhook_wrap(func,*a,**k):
828 def dhook_wrap(func,*a,**k):
823 """Wrap a function call in a sys.displayhook controller.
829 """Wrap a function call in a sys.displayhook controller.
824
830
825 Returns a wrapper around func which calls func, with all its arguments and
831 Returns a wrapper around func which calls func, with all its arguments and
826 keywords unmodified, using the default sys.displayhook. Since IPython
832 keywords unmodified, using the default sys.displayhook. Since IPython
827 modifies sys.displayhook, it breaks the behavior of certain systems that
833 modifies sys.displayhook, it breaks the behavior of certain systems that
828 rely on the default behavior, notably doctest.
834 rely on the default behavior, notably doctest.
829 """
835 """
830
836
831 def f(*a,**k):
837 def f(*a,**k):
832
838
833 dhook_s = sys.displayhook
839 dhook_s = sys.displayhook
834 sys.displayhook = sys.__displayhook__
840 sys.displayhook = sys.__displayhook__
835 try:
841 try:
836 out = func(*a,**k)
842 out = func(*a,**k)
837 finally:
843 finally:
838 sys.displayhook = dhook_s
844 sys.displayhook = dhook_s
839
845
840 return out
846 return out
841
847
842 f.__doc__ = func.__doc__
848 f.__doc__ = func.__doc__
843 return f
849 return f
844
850
845 #----------------------------------------------------------------------------
851 #----------------------------------------------------------------------------
846 def doctest_reload():
852 def doctest_reload():
847 """Properly reload doctest to reuse it interactively.
853 """Properly reload doctest to reuse it interactively.
848
854
849 This routine:
855 This routine:
850
856
851 - reloads doctest
857 - reloads doctest
852
858
853 - resets its global 'master' attribute to None, so that multiple uses of
859 - resets its global 'master' attribute to None, so that multiple uses of
854 the module interactively don't produce cumulative reports.
860 the module interactively don't produce cumulative reports.
855
861
856 - Monkeypatches its core test runner method to protect it from IPython's
862 - Monkeypatches its core test runner method to protect it from IPython's
857 modified displayhook. Doctest expects the default displayhook behavior
863 modified displayhook. Doctest expects the default displayhook behavior
858 deep down, so our modification breaks it completely. For this reason, a
864 deep down, so our modification breaks it completely. For this reason, a
859 hard monkeypatch seems like a reasonable solution rather than asking
865 hard monkeypatch seems like a reasonable solution rather than asking
860 users to manually use a different doctest runner when under IPython."""
866 users to manually use a different doctest runner when under IPython."""
861
867
862 import doctest
868 import doctest
863 reload(doctest)
869 reload(doctest)
864 doctest.master=None
870 doctest.master=None
865
871
866 try:
872 try:
867 doctest.DocTestRunner
873 doctest.DocTestRunner
868 except AttributeError:
874 except AttributeError:
869 # This is only for python 2.3 compatibility, remove once we move to
875 # This is only for python 2.3 compatibility, remove once we move to
870 # 2.4 only.
876 # 2.4 only.
871 pass
877 pass
872 else:
878 else:
873 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
879 doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run)
874
880
875 #----------------------------------------------------------------------------
881 #----------------------------------------------------------------------------
876 class HomeDirError(Error):
882 class HomeDirError(Error):
877 pass
883 pass
878
884
879 def get_home_dir():
885 def get_home_dir():
880 """Return the closest possible equivalent to a 'home' directory.
886 """Return the closest possible equivalent to a 'home' directory.
881
887
882 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
888 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
883
889
884 Currently only Posix and NT are implemented, a HomeDirError exception is
890 Currently only Posix and NT are implemented, a HomeDirError exception is
885 raised for all other OSes. """
891 raised for all other OSes. """
886
892
887 isdir = os.path.isdir
893 isdir = os.path.isdir
888 env = os.environ
894 env = os.environ
889
895
890 # first, check py2exe distribution root directory for _ipython.
896 # first, check py2exe distribution root directory for _ipython.
891 # This overrides all. Normally does not exist.
897 # This overrides all. Normally does not exist.
892
898
893 if '\\library.zip\\' in IPython.__file__.lower():
899 if '\\library.zip\\' in IPython.__file__.lower():
894 root, rest = IPython.__file__.lower().split('library.zip')
900 root, rest = IPython.__file__.lower().split('library.zip')
895 if isdir(root + '_ipython'):
901 if isdir(root + '_ipython'):
896 os.environ["IPYKITROOT"] = root.rstrip('\\')
902 os.environ["IPYKITROOT"] = root.rstrip('\\')
897 return root
903 return root
898
904
899 try:
905 try:
900 homedir = env['HOME']
906 homedir = env['HOME']
901 if not isdir(homedir):
907 if not isdir(homedir):
902 # in case a user stuck some string which does NOT resolve to a
908 # in case a user stuck some string which does NOT resolve to a
903 # valid path, it's as good as if we hadn't foud it
909 # valid path, it's as good as if we hadn't foud it
904 raise KeyError
910 raise KeyError
905 return homedir
911 return homedir
906 except KeyError:
912 except KeyError:
907 if os.name == 'posix':
913 if os.name == 'posix':
908 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
914 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
909 elif os.name == 'nt':
915 elif os.name == 'nt':
910 # For some strange reason, win9x returns 'nt' for os.name.
916 # For some strange reason, win9x returns 'nt' for os.name.
911 try:
917 try:
912 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
918 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
913 if not isdir(homedir):
919 if not isdir(homedir):
914 homedir = os.path.join(env['USERPROFILE'])
920 homedir = os.path.join(env['USERPROFILE'])
915 if not isdir(homedir):
921 if not isdir(homedir):
916 raise HomeDirError
922 raise HomeDirError
917 return homedir
923 return homedir
918 except:
924 except:
919 try:
925 try:
920 # Use the registry to get the 'My Documents' folder.
926 # Use the registry to get the 'My Documents' folder.
921 import _winreg as wreg
927 import _winreg as wreg
922 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
928 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
923 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
929 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
924 homedir = wreg.QueryValueEx(key,'Personal')[0]
930 homedir = wreg.QueryValueEx(key,'Personal')[0]
925 key.Close()
931 key.Close()
926 if not isdir(homedir):
932 if not isdir(homedir):
927 e = ('Invalid "Personal" folder registry key '
933 e = ('Invalid "Personal" folder registry key '
928 'typically "My Documents".\n'
934 'typically "My Documents".\n'
929 'Value: %s\n'
935 'Value: %s\n'
930 'This is not a valid directory on your system.' %
936 'This is not a valid directory on your system.' %
931 homedir)
937 homedir)
932 raise HomeDirError(e)
938 raise HomeDirError(e)
933 return homedir
939 return homedir
934 except HomeDirError:
940 except HomeDirError:
935 raise
941 raise
936 except:
942 except:
937 return 'C:\\'
943 return 'C:\\'
938 elif os.name == 'dos':
944 elif os.name == 'dos':
939 # Desperate, may do absurd things in classic MacOS. May work under DOS.
945 # Desperate, may do absurd things in classic MacOS. May work under DOS.
940 return 'C:\\'
946 return 'C:\\'
941 else:
947 else:
942 raise HomeDirError,'support for your operating system not implemented.'
948 raise HomeDirError,'support for your operating system not implemented.'
943
949
944 #****************************************************************************
950 #****************************************************************************
945 # strings and text
951 # strings and text
946
952
947 class LSString(str):
953 class LSString(str):
948 """String derivative with a special access attributes.
954 """String derivative with a special access attributes.
949
955
950 These are normal strings, but with the special attributes:
956 These are normal strings, but with the special attributes:
951
957
952 .l (or .list) : value as list (split on newlines).
958 .l (or .list) : value as list (split on newlines).
953 .n (or .nlstr): original value (the string itself).
959 .n (or .nlstr): original value (the string itself).
954 .s (or .spstr): value as whitespace-separated string.
960 .s (or .spstr): value as whitespace-separated string.
955 .p (or .paths): list of path objects
961 .p (or .paths): list of path objects
956
962
957 Any values which require transformations are computed only once and
963 Any values which require transformations are computed only once and
958 cached.
964 cached.
959
965
960 Such strings are very useful to efficiently interact with the shell, which
966 Such strings are very useful to efficiently interact with the shell, which
961 typically only understands whitespace-separated options for commands."""
967 typically only understands whitespace-separated options for commands."""
962
968
963 def get_list(self):
969 def get_list(self):
964 try:
970 try:
965 return self.__list
971 return self.__list
966 except AttributeError:
972 except AttributeError:
967 self.__list = self.split('\n')
973 self.__list = self.split('\n')
968 return self.__list
974 return self.__list
969
975
970 l = list = property(get_list)
976 l = list = property(get_list)
971
977
972 def get_spstr(self):
978 def get_spstr(self):
973 try:
979 try:
974 return self.__spstr
980 return self.__spstr
975 except AttributeError:
981 except AttributeError:
976 self.__spstr = self.replace('\n',' ')
982 self.__spstr = self.replace('\n',' ')
977 return self.__spstr
983 return self.__spstr
978
984
979 s = spstr = property(get_spstr)
985 s = spstr = property(get_spstr)
980
986
981 def get_nlstr(self):
987 def get_nlstr(self):
982 return self
988 return self
983
989
984 n = nlstr = property(get_nlstr)
990 n = nlstr = property(get_nlstr)
985
991
986 def get_paths(self):
992 def get_paths(self):
987 try:
993 try:
988 return self.__paths
994 return self.__paths
989 except AttributeError:
995 except AttributeError:
990 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
996 self.__paths = [path(p) for p in self.split('\n') if os.path.exists(p)]
991 return self.__paths
997 return self.__paths
992
998
993 p = paths = property(get_paths)
999 p = paths = property(get_paths)
994
1000
995 def print_lsstring(arg):
1001 def print_lsstring(arg):
996 """ Prettier (non-repr-like) and more informative printer for LSString """
1002 """ Prettier (non-repr-like) and more informative printer for LSString """
997 print "LSString (.p, .n, .l, .s available). Value:"
1003 print "LSString (.p, .n, .l, .s available). Value:"
998 print arg
1004 print arg
999
1005
1000 print_lsstring = result_display.when_type(LSString)(print_lsstring)
1006 print_lsstring = result_display.when_type(LSString)(print_lsstring)
1001
1007
1002 #----------------------------------------------------------------------------
1008 #----------------------------------------------------------------------------
1003 class SList(list):
1009 class SList(list):
1004 """List derivative with a special access attributes.
1010 """List derivative with a special access attributes.
1005
1011
1006 These are normal lists, but with the special attributes:
1012 These are normal lists, but with the special attributes:
1007
1013
1008 .l (or .list) : value as list (the list itself).
1014 .l (or .list) : value as list (the list itself).
1009 .n (or .nlstr): value as a string, joined on newlines.
1015 .n (or .nlstr): value as a string, joined on newlines.
1010 .s (or .spstr): value as a string, joined on spaces.
1016 .s (or .spstr): value as a string, joined on spaces.
1011 .p (or .paths): list of path objects
1017 .p (or .paths): list of path objects
1012
1018
1013 Any values which require transformations are computed only once and
1019 Any values which require transformations are computed only once and
1014 cached."""
1020 cached."""
1015
1021
1016 def get_list(self):
1022 def get_list(self):
1017 return self
1023 return self
1018
1024
1019 l = list = property(get_list)
1025 l = list = property(get_list)
1020
1026
1021 def get_spstr(self):
1027 def get_spstr(self):
1022 try:
1028 try:
1023 return self.__spstr
1029 return self.__spstr
1024 except AttributeError:
1030 except AttributeError:
1025 self.__spstr = ' '.join(self)
1031 self.__spstr = ' '.join(self)
1026 return self.__spstr
1032 return self.__spstr
1027
1033
1028 s = spstr = property(get_spstr)
1034 s = spstr = property(get_spstr)
1029
1035
1030 def get_nlstr(self):
1036 def get_nlstr(self):
1031 try:
1037 try:
1032 return self.__nlstr
1038 return self.__nlstr
1033 except AttributeError:
1039 except AttributeError:
1034 self.__nlstr = '\n'.join(self)
1040 self.__nlstr = '\n'.join(self)
1035 return self.__nlstr
1041 return self.__nlstr
1036
1042
1037 n = nlstr = property(get_nlstr)
1043 n = nlstr = property(get_nlstr)
1038
1044
1039 def get_paths(self):
1045 def get_paths(self):
1040 try:
1046 try:
1041 return self.__paths
1047 return self.__paths
1042 except AttributeError:
1048 except AttributeError:
1043 self.__paths = [path(p) for p in self if os.path.exists(p)]
1049 self.__paths = [path(p) for p in self if os.path.exists(p)]
1044 return self.__paths
1050 return self.__paths
1045
1051
1046 p = paths = property(get_paths)
1052 p = paths = property(get_paths)
1047
1053
1048 def grep(self, pattern, prune = False):
1054 def grep(self, pattern, prune = False):
1049 """ Return all strings matching 'pattern' (a regex or callable)
1055 """ Return all strings matching 'pattern' (a regex or callable)
1050
1056
1051 This is case-insensitive. If prune is true, return all items
1057 This is case-insensitive. If prune is true, return all items
1052 NOT matching the pattern.
1058 NOT matching the pattern.
1053
1059
1054 Examples::
1060 Examples::
1055
1061
1056 a.grep( lambda x: x.startswith('C') )
1062 a.grep( lambda x: x.startswith('C') )
1057 a.grep('Cha.*log', prune=1)
1063 a.grep('Cha.*log', prune=1)
1058 """
1064 """
1059 if isinstance(pattern, basestring):
1065 if isinstance(pattern, basestring):
1060 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
1066 pred = lambda x : re.search(pattern, x, re.IGNORECASE)
1061 else:
1067 else:
1062 pred = pattern
1068 pred = pattern
1063 if not prune:
1069 if not prune:
1064 return SList([el for el in self if pred(el)])
1070 return SList([el for el in self if pred(el)])
1065 else:
1071 else:
1066 return SList([el for el in self if not pred(el)])
1072 return SList([el for el in self if not pred(el)])
1067 def fields(self, *fields):
1073 def fields(self, *fields):
1068 """ Collect whitespace-separated fields from string list
1074 """ Collect whitespace-separated fields from string list
1069
1075
1070 Allows quick awk-like usage of string lists.
1076 Allows quick awk-like usage of string lists.
1071
1077
1072 Example data (in var a, created by 'a = !ls -l')::
1078 Example data (in var a, created by 'a = !ls -l')::
1073 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
1079 -rwxrwxrwx 1 ville None 18 Dec 14 2006 ChangeLog
1074 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
1080 drwxrwxrwx+ 6 ville None 0 Oct 24 18:05 IPython
1075
1081
1076 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
1082 a.fields(0) is ['-rwxrwxrwx', 'drwxrwxrwx+']
1077 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
1083 a.fields(1,0) is ['1 -rwxrwxrwx', '6 drwxrwxrwx+']
1078 (note the joining by space).
1084 (note the joining by space).
1079
1085
1080 IndexErrors are ignored.
1086 IndexErrors are ignored.
1081
1087
1082 Without args, fields() just split()'s the strings.
1088 Without args, fields() just split()'s the strings.
1083 """
1089 """
1084 if len(fields) == 0:
1090 if len(fields) == 0:
1085 return [el.split() for el in self]
1091 return [el.split() for el in self]
1086
1092
1087 res = SList()
1093 res = SList()
1088 for el in [f.split() for f in self]:
1094 for el in [f.split() for f in self]:
1089 lineparts = []
1095 lineparts = []
1090
1096
1091 for fd in fields:
1097 for fd in fields:
1092 try:
1098 try:
1093 lineparts.append(el[fd])
1099 lineparts.append(el[fd])
1094 except IndexError:
1100 except IndexError:
1095 pass
1101 pass
1096 if lineparts:
1102 if lineparts:
1097 res.append(" ".join(lineparts))
1103 res.append(" ".join(lineparts))
1098
1104
1099 return res
1105 return res
1100
1106
1101
1107
1102
1108
1103
1109
1104
1110
1105 def print_slist(arg):
1111 def print_slist(arg):
1106 """ Prettier (non-repr-like) and more informative printer for SList """
1112 """ Prettier (non-repr-like) and more informative printer for SList """
1107 print "SList (.p, .n, .l, .s, .grep(), .fields() available). Value:"
1113 print "SList (.p, .n, .l, .s, .grep(), .fields() available). Value:"
1108 nlprint(arg)
1114 nlprint(arg)
1109
1115
1110 print_slist = result_display.when_type(SList)(print_slist)
1116 print_slist = result_display.when_type(SList)(print_slist)
1111
1117
1112
1118
1113
1119
1114 #----------------------------------------------------------------------------
1120 #----------------------------------------------------------------------------
1115 def esc_quotes(strng):
1121 def esc_quotes(strng):
1116 """Return the input string with single and double quotes escaped out"""
1122 """Return the input string with single and double quotes escaped out"""
1117
1123
1118 return strng.replace('"','\\"').replace("'","\\'")
1124 return strng.replace('"','\\"').replace("'","\\'")
1119
1125
1120 #----------------------------------------------------------------------------
1126 #----------------------------------------------------------------------------
1121 def make_quoted_expr(s):
1127 def make_quoted_expr(s):
1122 """Return string s in appropriate quotes, using raw string if possible.
1128 """Return string s in appropriate quotes, using raw string if possible.
1123
1129
1124 Effectively this turns string: cd \ao\ao\
1130 Effectively this turns string: cd \ao\ao\
1125 to: r"cd \ao\ao\_"[:-1]
1131 to: r"cd \ao\ao\_"[:-1]
1126
1132
1127 Note the use of raw string and padding at the end to allow trailing backslash.
1133 Note the use of raw string and padding at the end to allow trailing backslash.
1128
1134
1129 """
1135 """
1130
1136
1131 tail = ''
1137 tail = ''
1132 tailpadding = ''
1138 tailpadding = ''
1133 raw = ''
1139 raw = ''
1134 if "\\" in s:
1140 if "\\" in s:
1135 raw = 'r'
1141 raw = 'r'
1136 if s.endswith('\\'):
1142 if s.endswith('\\'):
1137 tail = '[:-1]'
1143 tail = '[:-1]'
1138 tailpadding = '_'
1144 tailpadding = '_'
1139 if '"' not in s:
1145 if '"' not in s:
1140 quote = '"'
1146 quote = '"'
1141 elif "'" not in s:
1147 elif "'" not in s:
1142 quote = "'"
1148 quote = "'"
1143 elif '"""' not in s and not s.endswith('"'):
1149 elif '"""' not in s and not s.endswith('"'):
1144 quote = '"""'
1150 quote = '"""'
1145 elif "'''" not in s and not s.endswith("'"):
1151 elif "'''" not in s and not s.endswith("'"):
1146 quote = "'''"
1152 quote = "'''"
1147 else:
1153 else:
1148 # give up, backslash-escaped string will do
1154 # give up, backslash-escaped string will do
1149 return '"%s"' % esc_quotes(s)
1155 return '"%s"' % esc_quotes(s)
1150 res = raw + quote + s + tailpadding + quote + tail
1156 res = raw + quote + s + tailpadding + quote + tail
1151 return res
1157 return res
1152
1158
1153
1159
1154 #----------------------------------------------------------------------------
1160 #----------------------------------------------------------------------------
1155 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1161 def raw_input_multi(header='', ps1='==> ', ps2='..> ',terminate_str = '.'):
1156 """Take multiple lines of input.
1162 """Take multiple lines of input.
1157
1163
1158 A list with each line of input as a separate element is returned when a
1164 A list with each line of input as a separate element is returned when a
1159 termination string is entered (defaults to a single '.'). Input can also
1165 termination string is entered (defaults to a single '.'). Input can also
1160 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1166 terminate via EOF (^D in Unix, ^Z-RET in Windows).
1161
1167
1162 Lines of input which end in \\ are joined into single entries (and a
1168 Lines of input which end in \\ are joined into single entries (and a
1163 secondary continuation prompt is issued as long as the user terminates
1169 secondary continuation prompt is issued as long as the user terminates
1164 lines with \\). This allows entering very long strings which are still
1170 lines with \\). This allows entering very long strings which are still
1165 meant to be treated as single entities.
1171 meant to be treated as single entities.
1166 """
1172 """
1167
1173
1168 try:
1174 try:
1169 if header:
1175 if header:
1170 header += '\n'
1176 header += '\n'
1171 lines = [raw_input(header + ps1)]
1177 lines = [raw_input(header + ps1)]
1172 except EOFError:
1178 except EOFError:
1173 return []
1179 return []
1174 terminate = [terminate_str]
1180 terminate = [terminate_str]
1175 try:
1181 try:
1176 while lines[-1:] != terminate:
1182 while lines[-1:] != terminate:
1177 new_line = raw_input(ps1)
1183 new_line = raw_input(ps1)
1178 while new_line.endswith('\\'):
1184 while new_line.endswith('\\'):
1179 new_line = new_line[:-1] + raw_input(ps2)
1185 new_line = new_line[:-1] + raw_input(ps2)
1180 lines.append(new_line)
1186 lines.append(new_line)
1181
1187
1182 return lines[:-1] # don't return the termination command
1188 return lines[:-1] # don't return the termination command
1183 except EOFError:
1189 except EOFError:
1184 print
1190 print
1185 return lines
1191 return lines
1186
1192
1187 #----------------------------------------------------------------------------
1193 #----------------------------------------------------------------------------
1188 def raw_input_ext(prompt='', ps2='... '):
1194 def raw_input_ext(prompt='', ps2='... '):
1189 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1195 """Similar to raw_input(), but accepts extended lines if input ends with \\."""
1190
1196
1191 line = raw_input(prompt)
1197 line = raw_input(prompt)
1192 while line.endswith('\\'):
1198 while line.endswith('\\'):
1193 line = line[:-1] + raw_input(ps2)
1199 line = line[:-1] + raw_input(ps2)
1194 return line
1200 return line
1195
1201
1196 #----------------------------------------------------------------------------
1202 #----------------------------------------------------------------------------
1197 def ask_yes_no(prompt,default=None):
1203 def ask_yes_no(prompt,default=None):
1198 """Asks a question and returns a boolean (y/n) answer.
1204 """Asks a question and returns a boolean (y/n) answer.
1199
1205
1200 If default is given (one of 'y','n'), it is used if the user input is
1206 If default is given (one of 'y','n'), it is used if the user input is
1201 empty. Otherwise the question is repeated until an answer is given.
1207 empty. Otherwise the question is repeated until an answer is given.
1202
1208
1203 An EOF is treated as the default answer. If there is no default, an
1209 An EOF is treated as the default answer. If there is no default, an
1204 exception is raised to prevent infinite loops.
1210 exception is raised to prevent infinite loops.
1205
1211
1206 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1212 Valid answers are: y/yes/n/no (match is not case sensitive)."""
1207
1213
1208 answers = {'y':True,'n':False,'yes':True,'no':False}
1214 answers = {'y':True,'n':False,'yes':True,'no':False}
1209 ans = None
1215 ans = None
1210 while ans not in answers.keys():
1216 while ans not in answers.keys():
1211 try:
1217 try:
1212 ans = raw_input(prompt+' ').lower()
1218 ans = raw_input(prompt+' ').lower()
1213 if not ans: # response was an empty string
1219 if not ans: # response was an empty string
1214 ans = default
1220 ans = default
1215 except KeyboardInterrupt:
1221 except KeyboardInterrupt:
1216 pass
1222 pass
1217 except EOFError:
1223 except EOFError:
1218 if default in answers.keys():
1224 if default in answers.keys():
1219 ans = default
1225 ans = default
1220 print
1226 print
1221 else:
1227 else:
1222 raise
1228 raise
1223
1229
1224 return answers[ans]
1230 return answers[ans]
1225
1231
1226 #----------------------------------------------------------------------------
1232 #----------------------------------------------------------------------------
1227 def marquee(txt='',width=78,mark='*'):
1233 def marquee(txt='',width=78,mark='*'):
1228 """Return the input string centered in a 'marquee'."""
1234 """Return the input string centered in a 'marquee'."""
1229 if not txt:
1235 if not txt:
1230 return (mark*width)[:width]
1236 return (mark*width)[:width]
1231 nmark = (width-len(txt)-2)/len(mark)/2
1237 nmark = (width-len(txt)-2)/len(mark)/2
1232 if nmark < 0: nmark =0
1238 if nmark < 0: nmark =0
1233 marks = mark*nmark
1239 marks = mark*nmark
1234 return '%s %s %s' % (marks,txt,marks)
1240 return '%s %s %s' % (marks,txt,marks)
1235
1241
1236 #----------------------------------------------------------------------------
1242 #----------------------------------------------------------------------------
1237 class EvalDict:
1243 class EvalDict:
1238 """
1244 """
1239 Emulate a dict which evaluates its contents in the caller's frame.
1245 Emulate a dict which evaluates its contents in the caller's frame.
1240
1246
1241 Usage:
1247 Usage:
1242 >>>number = 19
1248 >>>number = 19
1243 >>>text = "python"
1249 >>>text = "python"
1244 >>>print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1250 >>>print "%(text.capitalize())s %(number/9.0).1f rules!" % EvalDict()
1245 """
1251 """
1246
1252
1247 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1253 # This version is due to sismex01@hebmex.com on c.l.py, and is basically a
1248 # modified (shorter) version of:
1254 # modified (shorter) version of:
1249 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1255 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66018 by
1250 # Skip Montanaro (skip@pobox.com).
1256 # Skip Montanaro (skip@pobox.com).
1251
1257
1252 def __getitem__(self, name):
1258 def __getitem__(self, name):
1253 frame = sys._getframe(1)
1259 frame = sys._getframe(1)
1254 return eval(name, frame.f_globals, frame.f_locals)
1260 return eval(name, frame.f_globals, frame.f_locals)
1255
1261
1256 EvalString = EvalDict # for backwards compatibility
1262 EvalString = EvalDict # for backwards compatibility
1257 #----------------------------------------------------------------------------
1263 #----------------------------------------------------------------------------
1258 def qw(words,flat=0,sep=None,maxsplit=-1):
1264 def qw(words,flat=0,sep=None,maxsplit=-1):
1259 """Similar to Perl's qw() operator, but with some more options.
1265 """Similar to Perl's qw() operator, but with some more options.
1260
1266
1261 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1267 qw(words,flat=0,sep=' ',maxsplit=-1) -> words.split(sep,maxsplit)
1262
1268
1263 words can also be a list itself, and with flat=1, the output will be
1269 words can also be a list itself, and with flat=1, the output will be
1264 recursively flattened. Examples:
1270 recursively flattened. Examples:
1265
1271
1266 >>> qw('1 2')
1272 >>> qw('1 2')
1267 ['1', '2']
1273 ['1', '2']
1268 >>> qw(['a b','1 2',['m n','p q']])
1274 >>> qw(['a b','1 2',['m n','p q']])
1269 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1275 [['a', 'b'], ['1', '2'], [['m', 'n'], ['p', 'q']]]
1270 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1276 >>> qw(['a b','1 2',['m n','p q']],flat=1)
1271 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q'] """
1277 ['a', 'b', '1', '2', 'm', 'n', 'p', 'q'] """
1272
1278
1273 if type(words) in StringTypes:
1279 if type(words) in StringTypes:
1274 return [word.strip() for word in words.split(sep,maxsplit)
1280 return [word.strip() for word in words.split(sep,maxsplit)
1275 if word and not word.isspace() ]
1281 if word and not word.isspace() ]
1276 if flat:
1282 if flat:
1277 return flatten(map(qw,words,[1]*len(words)))
1283 return flatten(map(qw,words,[1]*len(words)))
1278 return map(qw,words)
1284 return map(qw,words)
1279
1285
1280 #----------------------------------------------------------------------------
1286 #----------------------------------------------------------------------------
1281 def qwflat(words,sep=None,maxsplit=-1):
1287 def qwflat(words,sep=None,maxsplit=-1):
1282 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1288 """Calls qw(words) in flat mode. It's just a convenient shorthand."""
1283 return qw(words,1,sep,maxsplit)
1289 return qw(words,1,sep,maxsplit)
1284
1290
1285 #----------------------------------------------------------------------------
1291 #----------------------------------------------------------------------------
1286 def qw_lol(indata):
1292 def qw_lol(indata):
1287 """qw_lol('a b') -> [['a','b']],
1293 """qw_lol('a b') -> [['a','b']],
1288 otherwise it's just a call to qw().
1294 otherwise it's just a call to qw().
1289
1295
1290 We need this to make sure the modules_some keys *always* end up as a
1296 We need this to make sure the modules_some keys *always* end up as a
1291 list of lists."""
1297 list of lists."""
1292
1298
1293 if type(indata) in StringTypes:
1299 if type(indata) in StringTypes:
1294 return [qw(indata)]
1300 return [qw(indata)]
1295 else:
1301 else:
1296 return qw(indata)
1302 return qw(indata)
1297
1303
1298 #-----------------------------------------------------------------------------
1304 #-----------------------------------------------------------------------------
1299 def list_strings(arg):
1305 def list_strings(arg):
1300 """Always return a list of strings, given a string or list of strings
1306 """Always return a list of strings, given a string or list of strings
1301 as input."""
1307 as input."""
1302
1308
1303 if type(arg) in StringTypes: return [arg]
1309 if type(arg) in StringTypes: return [arg]
1304 else: return arg
1310 else: return arg
1305
1311
1306 #----------------------------------------------------------------------------
1312 #----------------------------------------------------------------------------
1307 def grep(pat,list,case=1):
1313 def grep(pat,list,case=1):
1308 """Simple minded grep-like function.
1314 """Simple minded grep-like function.
1309 grep(pat,list) returns occurrences of pat in list, None on failure.
1315 grep(pat,list) returns occurrences of pat in list, None on failure.
1310
1316
1311 It only does simple string matching, with no support for regexps. Use the
1317 It only does simple string matching, with no support for regexps. Use the
1312 option case=0 for case-insensitive matching."""
1318 option case=0 for case-insensitive matching."""
1313
1319
1314 # This is pretty crude. At least it should implement copying only references
1320 # This is pretty crude. At least it should implement copying only references
1315 # to the original data in case it's big. Now it copies the data for output.
1321 # to the original data in case it's big. Now it copies the data for output.
1316 out=[]
1322 out=[]
1317 if case:
1323 if case:
1318 for term in list:
1324 for term in list:
1319 if term.find(pat)>-1: out.append(term)
1325 if term.find(pat)>-1: out.append(term)
1320 else:
1326 else:
1321 lpat=pat.lower()
1327 lpat=pat.lower()
1322 for term in list:
1328 for term in list:
1323 if term.lower().find(lpat)>-1: out.append(term)
1329 if term.lower().find(lpat)>-1: out.append(term)
1324
1330
1325 if len(out): return out
1331 if len(out): return out
1326 else: return None
1332 else: return None
1327
1333
1328 #----------------------------------------------------------------------------
1334 #----------------------------------------------------------------------------
1329 def dgrep(pat,*opts):
1335 def dgrep(pat,*opts):
1330 """Return grep() on dir()+dir(__builtins__).
1336 """Return grep() on dir()+dir(__builtins__).
1331
1337
1332 A very common use of grep() when working interactively."""
1338 A very common use of grep() when working interactively."""
1333
1339
1334 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1340 return grep(pat,dir(__main__)+dir(__main__.__builtins__),*opts)
1335
1341
1336 #----------------------------------------------------------------------------
1342 #----------------------------------------------------------------------------
1337 def idgrep(pat):
1343 def idgrep(pat):
1338 """Case-insensitive dgrep()"""
1344 """Case-insensitive dgrep()"""
1339
1345
1340 return dgrep(pat,0)
1346 return dgrep(pat,0)
1341
1347
1342 #----------------------------------------------------------------------------
1348 #----------------------------------------------------------------------------
1343 def igrep(pat,list):
1349 def igrep(pat,list):
1344 """Synonym for case-insensitive grep."""
1350 """Synonym for case-insensitive grep."""
1345
1351
1346 return grep(pat,list,case=0)
1352 return grep(pat,list,case=0)
1347
1353
1348 #----------------------------------------------------------------------------
1354 #----------------------------------------------------------------------------
1349 def indent(str,nspaces=4,ntabs=0):
1355 def indent(str,nspaces=4,ntabs=0):
1350 """Indent a string a given number of spaces or tabstops.
1356 """Indent a string a given number of spaces or tabstops.
1351
1357
1352 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1358 indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces.
1353 """
1359 """
1354 if str is None:
1360 if str is None:
1355 return
1361 return
1356 ind = '\t'*ntabs+' '*nspaces
1362 ind = '\t'*ntabs+' '*nspaces
1357 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1363 outstr = '%s%s' % (ind,str.replace(os.linesep,os.linesep+ind))
1358 if outstr.endswith(os.linesep+ind):
1364 if outstr.endswith(os.linesep+ind):
1359 return outstr[:-len(ind)]
1365 return outstr[:-len(ind)]
1360 else:
1366 else:
1361 return outstr
1367 return outstr
1362
1368
1363 #-----------------------------------------------------------------------------
1369 #-----------------------------------------------------------------------------
1364 def native_line_ends(filename,backup=1):
1370 def native_line_ends(filename,backup=1):
1365 """Convert (in-place) a file to line-ends native to the current OS.
1371 """Convert (in-place) a file to line-ends native to the current OS.
1366
1372
1367 If the optional backup argument is given as false, no backup of the
1373 If the optional backup argument is given as false, no backup of the
1368 original file is left. """
1374 original file is left. """
1369
1375
1370 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1376 backup_suffixes = {'posix':'~','dos':'.bak','nt':'.bak','mac':'.bak'}
1371
1377
1372 bak_filename = filename + backup_suffixes[os.name]
1378 bak_filename = filename + backup_suffixes[os.name]
1373
1379
1374 original = open(filename).read()
1380 original = open(filename).read()
1375 shutil.copy2(filename,bak_filename)
1381 shutil.copy2(filename,bak_filename)
1376 try:
1382 try:
1377 new = open(filename,'wb')
1383 new = open(filename,'wb')
1378 new.write(os.linesep.join(original.splitlines()))
1384 new.write(os.linesep.join(original.splitlines()))
1379 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1385 new.write(os.linesep) # ALWAYS put an eol at the end of the file
1380 new.close()
1386 new.close()
1381 except:
1387 except:
1382 os.rename(bak_filename,filename)
1388 os.rename(bak_filename,filename)
1383 if not backup:
1389 if not backup:
1384 try:
1390 try:
1385 os.remove(bak_filename)
1391 os.remove(bak_filename)
1386 except:
1392 except:
1387 pass
1393 pass
1388
1394
1389 #----------------------------------------------------------------------------
1395 #----------------------------------------------------------------------------
1390 def get_pager_cmd(pager_cmd = None):
1396 def get_pager_cmd(pager_cmd = None):
1391 """Return a pager command.
1397 """Return a pager command.
1392
1398
1393 Makes some attempts at finding an OS-correct one."""
1399 Makes some attempts at finding an OS-correct one."""
1394
1400
1395 if os.name == 'posix':
1401 if os.name == 'posix':
1396 default_pager_cmd = 'less -r' # -r for color control sequences
1402 default_pager_cmd = 'less -r' # -r for color control sequences
1397 elif os.name in ['nt','dos']:
1403 elif os.name in ['nt','dos']:
1398 default_pager_cmd = 'type'
1404 default_pager_cmd = 'type'
1399
1405
1400 if pager_cmd is None:
1406 if pager_cmd is None:
1401 try:
1407 try:
1402 pager_cmd = os.environ['PAGER']
1408 pager_cmd = os.environ['PAGER']
1403 except:
1409 except:
1404 pager_cmd = default_pager_cmd
1410 pager_cmd = default_pager_cmd
1405 return pager_cmd
1411 return pager_cmd
1406
1412
1407 #-----------------------------------------------------------------------------
1413 #-----------------------------------------------------------------------------
1408 def get_pager_start(pager,start):
1414 def get_pager_start(pager,start):
1409 """Return the string for paging files with an offset.
1415 """Return the string for paging files with an offset.
1410
1416
1411 This is the '+N' argument which less and more (under Unix) accept.
1417 This is the '+N' argument which less and more (under Unix) accept.
1412 """
1418 """
1413
1419
1414 if pager in ['less','more']:
1420 if pager in ['less','more']:
1415 if start:
1421 if start:
1416 start_string = '+' + str(start)
1422 start_string = '+' + str(start)
1417 else:
1423 else:
1418 start_string = ''
1424 start_string = ''
1419 else:
1425 else:
1420 start_string = ''
1426 start_string = ''
1421 return start_string
1427 return start_string
1422
1428
1423 #----------------------------------------------------------------------------
1429 #----------------------------------------------------------------------------
1424 # (X)emacs on W32 doesn't like to be bypassed with msvcrt.getch()
1430 # (X)emacs on W32 doesn't like to be bypassed with msvcrt.getch()
1425 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
1431 if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs':
1426 import msvcrt
1432 import msvcrt
1427 def page_more():
1433 def page_more():
1428 """ Smart pausing between pages
1434 """ Smart pausing between pages
1429
1435
1430 @return: True if need print more lines, False if quit
1436 @return: True if need print more lines, False if quit
1431 """
1437 """
1432 Term.cout.write('---Return to continue, q to quit--- ')
1438 Term.cout.write('---Return to continue, q to quit--- ')
1433 ans = msvcrt.getch()
1439 ans = msvcrt.getch()
1434 if ans in ("q", "Q"):
1440 if ans in ("q", "Q"):
1435 result = False
1441 result = False
1436 else:
1442 else:
1437 result = True
1443 result = True
1438 Term.cout.write("\b"*37 + " "*37 + "\b"*37)
1444 Term.cout.write("\b"*37 + " "*37 + "\b"*37)
1439 return result
1445 return result
1440 else:
1446 else:
1441 def page_more():
1447 def page_more():
1442 ans = raw_input('---Return to continue, q to quit--- ')
1448 ans = raw_input('---Return to continue, q to quit--- ')
1443 if ans.lower().startswith('q'):
1449 if ans.lower().startswith('q'):
1444 return False
1450 return False
1445 else:
1451 else:
1446 return True
1452 return True
1447
1453
1448 esc_re = re.compile(r"(\x1b[^m]+m)")
1454 esc_re = re.compile(r"(\x1b[^m]+m)")
1449
1455
1450 def page_dumb(strng,start=0,screen_lines=25):
1456 def page_dumb(strng,start=0,screen_lines=25):
1451 """Very dumb 'pager' in Python, for when nothing else works.
1457 """Very dumb 'pager' in Python, for when nothing else works.
1452
1458
1453 Only moves forward, same interface as page(), except for pager_cmd and
1459 Only moves forward, same interface as page(), except for pager_cmd and
1454 mode."""
1460 mode."""
1455
1461
1456 out_ln = strng.splitlines()[start:]
1462 out_ln = strng.splitlines()[start:]
1457 screens = chop(out_ln,screen_lines-1)
1463 screens = chop(out_ln,screen_lines-1)
1458 if len(screens) == 1:
1464 if len(screens) == 1:
1459 print >>Term.cout, os.linesep.join(screens[0])
1465 print >>Term.cout, os.linesep.join(screens[0])
1460 else:
1466 else:
1461 last_escape = ""
1467 last_escape = ""
1462 for scr in screens[0:-1]:
1468 for scr in screens[0:-1]:
1463 hunk = os.linesep.join(scr)
1469 hunk = os.linesep.join(scr)
1464 print >>Term.cout, last_escape + hunk
1470 print >>Term.cout, last_escape + hunk
1465 if not page_more():
1471 if not page_more():
1466 return
1472 return
1467 esc_list = esc_re.findall(hunk)
1473 esc_list = esc_re.findall(hunk)
1468 if len(esc_list) > 0:
1474 if len(esc_list) > 0:
1469 last_escape = esc_list[-1]
1475 last_escape = esc_list[-1]
1470 print >>Term.cout, last_escape + os.linesep.join(screens[-1])
1476 print >>Term.cout, last_escape + os.linesep.join(screens[-1])
1471
1477
1472 #----------------------------------------------------------------------------
1478 #----------------------------------------------------------------------------
1473 def page(strng,start=0,screen_lines=0,pager_cmd = None):
1479 def page(strng,start=0,screen_lines=0,pager_cmd = None):
1474 """Print a string, piping through a pager after a certain length.
1480 """Print a string, piping through a pager after a certain length.
1475
1481
1476 The screen_lines parameter specifies the number of *usable* lines of your
1482 The screen_lines parameter specifies the number of *usable* lines of your
1477 terminal screen (total lines minus lines you need to reserve to show other
1483 terminal screen (total lines minus lines you need to reserve to show other
1478 information).
1484 information).
1479
1485
1480 If you set screen_lines to a number <=0, page() will try to auto-determine
1486 If you set screen_lines to a number <=0, page() will try to auto-determine
1481 your screen size and will only use up to (screen_size+screen_lines) for
1487 your screen size and will only use up to (screen_size+screen_lines) for
1482 printing, paging after that. That is, if you want auto-detection but need
1488 printing, paging after that. That is, if you want auto-detection but need
1483 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
1489 to reserve the bottom 3 lines of the screen, use screen_lines = -3, and for
1484 auto-detection without any lines reserved simply use screen_lines = 0.
1490 auto-detection without any lines reserved simply use screen_lines = 0.
1485
1491
1486 If a string won't fit in the allowed lines, it is sent through the
1492 If a string won't fit in the allowed lines, it is sent through the
1487 specified pager command. If none given, look for PAGER in the environment,
1493 specified pager command. If none given, look for PAGER in the environment,
1488 and ultimately default to less.
1494 and ultimately default to less.
1489
1495
1490 If no system pager works, the string is sent through a 'dumb pager'
1496 If no system pager works, the string is sent through a 'dumb pager'
1491 written in python, very simplistic.
1497 written in python, very simplistic.
1492 """
1498 """
1493
1499
1494 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
1500 # Ugly kludge, but calling curses.initscr() flat out crashes in emacs
1495 TERM = os.environ.get('TERM','dumb')
1501 TERM = os.environ.get('TERM','dumb')
1496 if TERM in ['dumb','emacs'] and os.name != 'nt':
1502 if TERM in ['dumb','emacs'] and os.name != 'nt':
1497 print strng
1503 print strng
1498 return
1504 return
1499 # chop off the topmost part of the string we don't want to see
1505 # chop off the topmost part of the string we don't want to see
1500 str_lines = strng.split(os.linesep)[start:]
1506 str_lines = strng.split(os.linesep)[start:]
1501 str_toprint = os.linesep.join(str_lines)
1507 str_toprint = os.linesep.join(str_lines)
1502 num_newlines = len(str_lines)
1508 num_newlines = len(str_lines)
1503 len_str = len(str_toprint)
1509 len_str = len(str_toprint)
1504
1510
1505 # Dumb heuristics to guesstimate number of on-screen lines the string
1511 # Dumb heuristics to guesstimate number of on-screen lines the string
1506 # takes. Very basic, but good enough for docstrings in reasonable
1512 # takes. Very basic, but good enough for docstrings in reasonable
1507 # terminals. If someone later feels like refining it, it's not hard.
1513 # terminals. If someone later feels like refining it, it's not hard.
1508 numlines = max(num_newlines,int(len_str/80)+1)
1514 numlines = max(num_newlines,int(len_str/80)+1)
1509
1515
1510 if os.name == "nt":
1516 if os.name == "nt":
1511 screen_lines_def = get_console_size(defaulty=25)[1]
1517 screen_lines_def = get_console_size(defaulty=25)[1]
1512 else:
1518 else:
1513 screen_lines_def = 25 # default value if we can't auto-determine
1519 screen_lines_def = 25 # default value if we can't auto-determine
1514
1520
1515 # auto-determine screen size
1521 # auto-determine screen size
1516 if screen_lines <= 0:
1522 if screen_lines <= 0:
1517 if TERM=='xterm':
1523 if TERM=='xterm':
1518 try:
1524 try:
1519 import curses
1525 import curses
1520 if hasattr(curses,'initscr'):
1526 if hasattr(curses,'initscr'):
1521 use_curses = 1
1527 use_curses = 1
1522 else:
1528 else:
1523 use_curses = 0
1529 use_curses = 0
1524 except ImportError:
1530 except ImportError:
1525 use_curses = 0
1531 use_curses = 0
1526 else:
1532 else:
1527 # curses causes problems on many terminals other than xterm.
1533 # curses causes problems on many terminals other than xterm.
1528 use_curses = 0
1534 use_curses = 0
1529 if use_curses:
1535 if use_curses:
1530 scr = curses.initscr()
1536 scr = curses.initscr()
1531 screen_lines_real,screen_cols = scr.getmaxyx()
1537 screen_lines_real,screen_cols = scr.getmaxyx()
1532 curses.endwin()
1538 curses.endwin()
1533 screen_lines += screen_lines_real
1539 screen_lines += screen_lines_real
1534 #print '***Screen size:',screen_lines_real,'lines x',\
1540 #print '***Screen size:',screen_lines_real,'lines x',\
1535 #screen_cols,'columns.' # dbg
1541 #screen_cols,'columns.' # dbg
1536 else:
1542 else:
1537 screen_lines += screen_lines_def
1543 screen_lines += screen_lines_def
1538
1544
1539 #print 'numlines',numlines,'screenlines',screen_lines # dbg
1545 #print 'numlines',numlines,'screenlines',screen_lines # dbg
1540 if numlines <= screen_lines :
1546 if numlines <= screen_lines :
1541 #print '*** normal print' # dbg
1547 #print '*** normal print' # dbg
1542 print >>Term.cout, str_toprint
1548 print >>Term.cout, str_toprint
1543 else:
1549 else:
1544 # Try to open pager and default to internal one if that fails.
1550 # Try to open pager and default to internal one if that fails.
1545 # All failure modes are tagged as 'retval=1', to match the return
1551 # All failure modes are tagged as 'retval=1', to match the return
1546 # value of a failed system command. If any intermediate attempt
1552 # value of a failed system command. If any intermediate attempt
1547 # sets retval to 1, at the end we resort to our own page_dumb() pager.
1553 # sets retval to 1, at the end we resort to our own page_dumb() pager.
1548 pager_cmd = get_pager_cmd(pager_cmd)
1554 pager_cmd = get_pager_cmd(pager_cmd)
1549 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1555 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1550 if os.name == 'nt':
1556 if os.name == 'nt':
1551 if pager_cmd.startswith('type'):
1557 if pager_cmd.startswith('type'):
1552 # The default WinXP 'type' command is failing on complex strings.
1558 # The default WinXP 'type' command is failing on complex strings.
1553 retval = 1
1559 retval = 1
1554 else:
1560 else:
1555 tmpname = tempfile.mktemp('.txt')
1561 tmpname = tempfile.mktemp('.txt')
1556 tmpfile = file(tmpname,'wt')
1562 tmpfile = file(tmpname,'wt')
1557 tmpfile.write(strng)
1563 tmpfile.write(strng)
1558 tmpfile.close()
1564 tmpfile.close()
1559 cmd = "%s < %s" % (pager_cmd,tmpname)
1565 cmd = "%s < %s" % (pager_cmd,tmpname)
1560 if os.system(cmd):
1566 if os.system(cmd):
1561 retval = 1
1567 retval = 1
1562 else:
1568 else:
1563 retval = None
1569 retval = None
1564 os.remove(tmpname)
1570 os.remove(tmpname)
1565 else:
1571 else:
1566 try:
1572 try:
1567 retval = None
1573 retval = None
1568 # if I use popen4, things hang. No idea why.
1574 # if I use popen4, things hang. No idea why.
1569 #pager,shell_out = os.popen4(pager_cmd)
1575 #pager,shell_out = os.popen4(pager_cmd)
1570 pager = os.popen(pager_cmd,'w')
1576 pager = os.popen(pager_cmd,'w')
1571 pager.write(strng)
1577 pager.write(strng)
1572 pager.close()
1578 pager.close()
1573 retval = pager.close() # success returns None
1579 retval = pager.close() # success returns None
1574 except IOError,msg: # broken pipe when user quits
1580 except IOError,msg: # broken pipe when user quits
1575 if msg.args == (32,'Broken pipe'):
1581 if msg.args == (32,'Broken pipe'):
1576 retval = None
1582 retval = None
1577 else:
1583 else:
1578 retval = 1
1584 retval = 1
1579 except OSError:
1585 except OSError:
1580 # Other strange problems, sometimes seen in Win2k/cygwin
1586 # Other strange problems, sometimes seen in Win2k/cygwin
1581 retval = 1
1587 retval = 1
1582 if retval is not None:
1588 if retval is not None:
1583 page_dumb(strng,screen_lines=screen_lines)
1589 page_dumb(strng,screen_lines=screen_lines)
1584
1590
1585 #----------------------------------------------------------------------------
1591 #----------------------------------------------------------------------------
1586 def page_file(fname,start = 0, pager_cmd = None):
1592 def page_file(fname,start = 0, pager_cmd = None):
1587 """Page a file, using an optional pager command and starting line.
1593 """Page a file, using an optional pager command and starting line.
1588 """
1594 """
1589
1595
1590 pager_cmd = get_pager_cmd(pager_cmd)
1596 pager_cmd = get_pager_cmd(pager_cmd)
1591 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1597 pager_cmd += ' ' + get_pager_start(pager_cmd,start)
1592
1598
1593 try:
1599 try:
1594 if os.environ['TERM'] in ['emacs','dumb']:
1600 if os.environ['TERM'] in ['emacs','dumb']:
1595 raise EnvironmentError
1601 raise EnvironmentError
1596 xsys(pager_cmd + ' ' + fname)
1602 xsys(pager_cmd + ' ' + fname)
1597 except:
1603 except:
1598 try:
1604 try:
1599 if start > 0:
1605 if start > 0:
1600 start -= 1
1606 start -= 1
1601 page(open(fname).read(),start)
1607 page(open(fname).read(),start)
1602 except:
1608 except:
1603 print 'Unable to show file',`fname`
1609 print 'Unable to show file',`fname`
1604
1610
1605 #----------------------------------------------------------------------------
1611 #----------------------------------------------------------------------------
1606 def snip_print(str,width = 75,print_full = 0,header = ''):
1612 def snip_print(str,width = 75,print_full = 0,header = ''):
1607 """Print a string snipping the midsection to fit in width.
1613 """Print a string snipping the midsection to fit in width.
1608
1614
1609 print_full: mode control:
1615 print_full: mode control:
1610 - 0: only snip long strings
1616 - 0: only snip long strings
1611 - 1: send to page() directly.
1617 - 1: send to page() directly.
1612 - 2: snip long strings and ask for full length viewing with page()
1618 - 2: snip long strings and ask for full length viewing with page()
1613 Return 1 if snipping was necessary, 0 otherwise."""
1619 Return 1 if snipping was necessary, 0 otherwise."""
1614
1620
1615 if print_full == 1:
1621 if print_full == 1:
1616 page(header+str)
1622 page(header+str)
1617 return 0
1623 return 0
1618
1624
1619 print header,
1625 print header,
1620 if len(str) < width:
1626 if len(str) < width:
1621 print str
1627 print str
1622 snip = 0
1628 snip = 0
1623 else:
1629 else:
1624 whalf = int((width -5)/2)
1630 whalf = int((width -5)/2)
1625 print str[:whalf] + ' <...> ' + str[-whalf:]
1631 print str[:whalf] + ' <...> ' + str[-whalf:]
1626 snip = 1
1632 snip = 1
1627 if snip and print_full == 2:
1633 if snip and print_full == 2:
1628 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
1634 if raw_input(header+' Snipped. View (y/n)? [N]').lower() == 'y':
1629 page(str)
1635 page(str)
1630 return snip
1636 return snip
1631
1637
1632 #****************************************************************************
1638 #****************************************************************************
1633 # lists, dicts and structures
1639 # lists, dicts and structures
1634
1640
1635 def belong(candidates,checklist):
1641 def belong(candidates,checklist):
1636 """Check whether a list of items appear in a given list of options.
1642 """Check whether a list of items appear in a given list of options.
1637
1643
1638 Returns a list of 1 and 0, one for each candidate given."""
1644 Returns a list of 1 and 0, one for each candidate given."""
1639
1645
1640 return [x in checklist for x in candidates]
1646 return [x in checklist for x in candidates]
1641
1647
1642 #----------------------------------------------------------------------------
1648 #----------------------------------------------------------------------------
1643 def uniq_stable(elems):
1649 def uniq_stable(elems):
1644 """uniq_stable(elems) -> list
1650 """uniq_stable(elems) -> list
1645
1651
1646 Return from an iterable, a list of all the unique elements in the input,
1652 Return from an iterable, a list of all the unique elements in the input,
1647 but maintaining the order in which they first appear.
1653 but maintaining the order in which they first appear.
1648
1654
1649 A naive solution to this problem which just makes a dictionary with the
1655 A naive solution to this problem which just makes a dictionary with the
1650 elements as keys fails to respect the stability condition, since
1656 elements as keys fails to respect the stability condition, since
1651 dictionaries are unsorted by nature.
1657 dictionaries are unsorted by nature.
1652
1658
1653 Note: All elements in the input must be valid dictionary keys for this
1659 Note: All elements in the input must be valid dictionary keys for this
1654 routine to work, as it internally uses a dictionary for efficiency
1660 routine to work, as it internally uses a dictionary for efficiency
1655 reasons."""
1661 reasons."""
1656
1662
1657 unique = []
1663 unique = []
1658 unique_dict = {}
1664 unique_dict = {}
1659 for nn in elems:
1665 for nn in elems:
1660 if nn not in unique_dict:
1666 if nn not in unique_dict:
1661 unique.append(nn)
1667 unique.append(nn)
1662 unique_dict[nn] = None
1668 unique_dict[nn] = None
1663 return unique
1669 return unique
1664
1670
1665 #----------------------------------------------------------------------------
1671 #----------------------------------------------------------------------------
1666 class NLprinter:
1672 class NLprinter:
1667 """Print an arbitrarily nested list, indicating index numbers.
1673 """Print an arbitrarily nested list, indicating index numbers.
1668
1674
1669 An instance of this class called nlprint is available and callable as a
1675 An instance of this class called nlprint is available and callable as a
1670 function.
1676 function.
1671
1677
1672 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1678 nlprint(list,indent=' ',sep=': ') -> prints indenting each level by 'indent'
1673 and using 'sep' to separate the index from the value. """
1679 and using 'sep' to separate the index from the value. """
1674
1680
1675 def __init__(self):
1681 def __init__(self):
1676 self.depth = 0
1682 self.depth = 0
1677
1683
1678 def __call__(self,lst,pos='',**kw):
1684 def __call__(self,lst,pos='',**kw):
1679 """Prints the nested list numbering levels."""
1685 """Prints the nested list numbering levels."""
1680 kw.setdefault('indent',' ')
1686 kw.setdefault('indent',' ')
1681 kw.setdefault('sep',': ')
1687 kw.setdefault('sep',': ')
1682 kw.setdefault('start',0)
1688 kw.setdefault('start',0)
1683 kw.setdefault('stop',len(lst))
1689 kw.setdefault('stop',len(lst))
1684 # we need to remove start and stop from kw so they don't propagate
1690 # we need to remove start and stop from kw so they don't propagate
1685 # into a recursive call for a nested list.
1691 # into a recursive call for a nested list.
1686 start = kw['start']; del kw['start']
1692 start = kw['start']; del kw['start']
1687 stop = kw['stop']; del kw['stop']
1693 stop = kw['stop']; del kw['stop']
1688 if self.depth == 0 and 'header' in kw.keys():
1694 if self.depth == 0 and 'header' in kw.keys():
1689 print kw['header']
1695 print kw['header']
1690
1696
1691 for idx in range(start,stop):
1697 for idx in range(start,stop):
1692 elem = lst[idx]
1698 elem = lst[idx]
1693 if type(elem)==type([]):
1699 if type(elem)==type([]):
1694 self.depth += 1
1700 self.depth += 1
1695 self.__call__(elem,itpl('$pos$idx,'),**kw)
1701 self.__call__(elem,itpl('$pos$idx,'),**kw)
1696 self.depth -= 1
1702 self.depth -= 1
1697 else:
1703 else:
1698 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1704 printpl(kw['indent']*self.depth+'$pos$idx$kw["sep"]$elem')
1699
1705
1700 nlprint = NLprinter()
1706 nlprint = NLprinter()
1701 #----------------------------------------------------------------------------
1707 #----------------------------------------------------------------------------
1702 def all_belong(candidates,checklist):
1708 def all_belong(candidates,checklist):
1703 """Check whether a list of items ALL appear in a given list of options.
1709 """Check whether a list of items ALL appear in a given list of options.
1704
1710
1705 Returns a single 1 or 0 value."""
1711 Returns a single 1 or 0 value."""
1706
1712
1707 return 1-(0 in [x in checklist for x in candidates])
1713 return 1-(0 in [x in checklist for x in candidates])
1708
1714
1709 #----------------------------------------------------------------------------
1715 #----------------------------------------------------------------------------
1710 def sort_compare(lst1,lst2,inplace = 1):
1716 def sort_compare(lst1,lst2,inplace = 1):
1711 """Sort and compare two lists.
1717 """Sort and compare two lists.
1712
1718
1713 By default it does it in place, thus modifying the lists. Use inplace = 0
1719 By default it does it in place, thus modifying the lists. Use inplace = 0
1714 to avoid that (at the cost of temporary copy creation)."""
1720 to avoid that (at the cost of temporary copy creation)."""
1715 if not inplace:
1721 if not inplace:
1716 lst1 = lst1[:]
1722 lst1 = lst1[:]
1717 lst2 = lst2[:]
1723 lst2 = lst2[:]
1718 lst1.sort(); lst2.sort()
1724 lst1.sort(); lst2.sort()
1719 return lst1 == lst2
1725 return lst1 == lst2
1720
1726
1721 #----------------------------------------------------------------------------
1727 #----------------------------------------------------------------------------
1722 def mkdict(**kwargs):
1728 def mkdict(**kwargs):
1723 """Return a dict from a keyword list.
1729 """Return a dict from a keyword list.
1724
1730
1725 It's just syntactic sugar for making ditcionary creation more convenient:
1731 It's just syntactic sugar for making ditcionary creation more convenient:
1726 # the standard way
1732 # the standard way
1727 >>>data = { 'red' : 1, 'green' : 2, 'blue' : 3 }
1733 >>>data = { 'red' : 1, 'green' : 2, 'blue' : 3 }
1728 # a cleaner way
1734 # a cleaner way
1729 >>>data = dict(red=1, green=2, blue=3)
1735 >>>data = dict(red=1, green=2, blue=3)
1730
1736
1731 If you need more than this, look at the Struct() class."""
1737 If you need more than this, look at the Struct() class."""
1732
1738
1733 return kwargs
1739 return kwargs
1734
1740
1735 #----------------------------------------------------------------------------
1741 #----------------------------------------------------------------------------
1736 def list2dict(lst):
1742 def list2dict(lst):
1737 """Takes a list of (key,value) pairs and turns it into a dict."""
1743 """Takes a list of (key,value) pairs and turns it into a dict."""
1738
1744
1739 dic = {}
1745 dic = {}
1740 for k,v in lst: dic[k] = v
1746 for k,v in lst: dic[k] = v
1741 return dic
1747 return dic
1742
1748
1743 #----------------------------------------------------------------------------
1749 #----------------------------------------------------------------------------
1744 def list2dict2(lst,default=''):
1750 def list2dict2(lst,default=''):
1745 """Takes a list and turns it into a dict.
1751 """Takes a list and turns it into a dict.
1746 Much slower than list2dict, but more versatile. This version can take
1752 Much slower than list2dict, but more versatile. This version can take
1747 lists with sublists of arbitrary length (including sclars)."""
1753 lists with sublists of arbitrary length (including sclars)."""
1748
1754
1749 dic = {}
1755 dic = {}
1750 for elem in lst:
1756 for elem in lst:
1751 if type(elem) in (types.ListType,types.TupleType):
1757 if type(elem) in (types.ListType,types.TupleType):
1752 size = len(elem)
1758 size = len(elem)
1753 if size == 0:
1759 if size == 0:
1754 pass
1760 pass
1755 elif size == 1:
1761 elif size == 1:
1756 dic[elem] = default
1762 dic[elem] = default
1757 else:
1763 else:
1758 k,v = elem[0], elem[1:]
1764 k,v = elem[0], elem[1:]
1759 if len(v) == 1: v = v[0]
1765 if len(v) == 1: v = v[0]
1760 dic[k] = v
1766 dic[k] = v
1761 else:
1767 else:
1762 dic[elem] = default
1768 dic[elem] = default
1763 return dic
1769 return dic
1764
1770
1765 #----------------------------------------------------------------------------
1771 #----------------------------------------------------------------------------
1766 def flatten(seq):
1772 def flatten(seq):
1767 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1773 """Flatten a list of lists (NOT recursive, only works for 2d lists)."""
1768
1774
1769 return [x for subseq in seq for x in subseq]
1775 return [x for subseq in seq for x in subseq]
1770
1776
1771 #----------------------------------------------------------------------------
1777 #----------------------------------------------------------------------------
1772 def get_slice(seq,start=0,stop=None,step=1):
1778 def get_slice(seq,start=0,stop=None,step=1):
1773 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1779 """Get a slice of a sequence with variable step. Specify start,stop,step."""
1774 if stop == None:
1780 if stop == None:
1775 stop = len(seq)
1781 stop = len(seq)
1776 item = lambda i: seq[i]
1782 item = lambda i: seq[i]
1777 return map(item,xrange(start,stop,step))
1783 return map(item,xrange(start,stop,step))
1778
1784
1779 #----------------------------------------------------------------------------
1785 #----------------------------------------------------------------------------
1780 def chop(seq,size):
1786 def chop(seq,size):
1781 """Chop a sequence into chunks of the given size."""
1787 """Chop a sequence into chunks of the given size."""
1782 chunk = lambda i: seq[i:i+size]
1788 chunk = lambda i: seq[i:i+size]
1783 return map(chunk,xrange(0,len(seq),size))
1789 return map(chunk,xrange(0,len(seq),size))
1784
1790
1785 #----------------------------------------------------------------------------
1791 #----------------------------------------------------------------------------
1786 # with is a keyword as of python 2.5, so this function is renamed to withobj
1792 # with is a keyword as of python 2.5, so this function is renamed to withobj
1787 # from its old 'with' name.
1793 # from its old 'with' name.
1788 def with_obj(object, **args):
1794 def with_obj(object, **args):
1789 """Set multiple attributes for an object, similar to Pascal's with.
1795 """Set multiple attributes for an object, similar to Pascal's with.
1790
1796
1791 Example:
1797 Example:
1792 with_obj(jim,
1798 with_obj(jim,
1793 born = 1960,
1799 born = 1960,
1794 haircolour = 'Brown',
1800 haircolour = 'Brown',
1795 eyecolour = 'Green')
1801 eyecolour = 'Green')
1796
1802
1797 Credit: Greg Ewing, in
1803 Credit: Greg Ewing, in
1798 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1804 http://mail.python.org/pipermail/python-list/2001-May/040703.html.
1799
1805
1800 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1806 NOTE: up until IPython 0.7.2, this was called simply 'with', but 'with'
1801 has become a keyword for Python 2.5, so we had to rename it."""
1807 has become a keyword for Python 2.5, so we had to rename it."""
1802
1808
1803 object.__dict__.update(args)
1809 object.__dict__.update(args)
1804
1810
1805 #----------------------------------------------------------------------------
1811 #----------------------------------------------------------------------------
1806 def setattr_list(obj,alist,nspace = None):
1812 def setattr_list(obj,alist,nspace = None):
1807 """Set a list of attributes for an object taken from a namespace.
1813 """Set a list of attributes for an object taken from a namespace.
1808
1814
1809 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1815 setattr_list(obj,alist,nspace) -> sets in obj all the attributes listed in
1810 alist with their values taken from nspace, which must be a dict (something
1816 alist with their values taken from nspace, which must be a dict (something
1811 like locals() will often do) If nspace isn't given, locals() of the
1817 like locals() will often do) If nspace isn't given, locals() of the
1812 *caller* is used, so in most cases you can omit it.
1818 *caller* is used, so in most cases you can omit it.
1813
1819
1814 Note that alist can be given as a string, which will be automatically
1820 Note that alist can be given as a string, which will be automatically
1815 split into a list on whitespace. If given as a list, it must be a list of
1821 split into a list on whitespace. If given as a list, it must be a list of
1816 *strings* (the variable names themselves), not of variables."""
1822 *strings* (the variable names themselves), not of variables."""
1817
1823
1818 # this grabs the local variables from the *previous* call frame -- that is
1824 # this grabs the local variables from the *previous* call frame -- that is
1819 # the locals from the function that called setattr_list().
1825 # the locals from the function that called setattr_list().
1820 # - snipped from weave.inline()
1826 # - snipped from weave.inline()
1821 if nspace is None:
1827 if nspace is None:
1822 call_frame = sys._getframe().f_back
1828 call_frame = sys._getframe().f_back
1823 nspace = call_frame.f_locals
1829 nspace = call_frame.f_locals
1824
1830
1825 if type(alist) in StringTypes:
1831 if type(alist) in StringTypes:
1826 alist = alist.split()
1832 alist = alist.split()
1827 for attr in alist:
1833 for attr in alist:
1828 val = eval(attr,nspace)
1834 val = eval(attr,nspace)
1829 setattr(obj,attr,val)
1835 setattr(obj,attr,val)
1830
1836
1831 #----------------------------------------------------------------------------
1837 #----------------------------------------------------------------------------
1832 def getattr_list(obj,alist,*args):
1838 def getattr_list(obj,alist,*args):
1833 """getattr_list(obj,alist[, default]) -> attribute list.
1839 """getattr_list(obj,alist[, default]) -> attribute list.
1834
1840
1835 Get a list of named attributes for an object. When a default argument is
1841 Get a list of named attributes for an object. When a default argument is
1836 given, it is returned when the attribute doesn't exist; without it, an
1842 given, it is returned when the attribute doesn't exist; without it, an
1837 exception is raised in that case.
1843 exception is raised in that case.
1838
1844
1839 Note that alist can be given as a string, which will be automatically
1845 Note that alist can be given as a string, which will be automatically
1840 split into a list on whitespace. If given as a list, it must be a list of
1846 split into a list on whitespace. If given as a list, it must be a list of
1841 *strings* (the variable names themselves), not of variables."""
1847 *strings* (the variable names themselves), not of variables."""
1842
1848
1843 if type(alist) in StringTypes:
1849 if type(alist) in StringTypes:
1844 alist = alist.split()
1850 alist = alist.split()
1845 if args:
1851 if args:
1846 if len(args)==1:
1852 if len(args)==1:
1847 default = args[0]
1853 default = args[0]
1848 return map(lambda attr: getattr(obj,attr,default),alist)
1854 return map(lambda attr: getattr(obj,attr,default),alist)
1849 else:
1855 else:
1850 raise ValueError,'getattr_list() takes only one optional argument'
1856 raise ValueError,'getattr_list() takes only one optional argument'
1851 else:
1857 else:
1852 return map(lambda attr: getattr(obj,attr),alist)
1858 return map(lambda attr: getattr(obj,attr),alist)
1853
1859
1854 #----------------------------------------------------------------------------
1860 #----------------------------------------------------------------------------
1855 def map_method(method,object_list,*argseq,**kw):
1861 def map_method(method,object_list,*argseq,**kw):
1856 """map_method(method,object_list,*args,**kw) -> list
1862 """map_method(method,object_list,*args,**kw) -> list
1857
1863
1858 Return a list of the results of applying the methods to the items of the
1864 Return a list of the results of applying the methods to the items of the
1859 argument sequence(s). If more than one sequence is given, the method is
1865 argument sequence(s). If more than one sequence is given, the method is
1860 called with an argument list consisting of the corresponding item of each
1866 called with an argument list consisting of the corresponding item of each
1861 sequence. All sequences must be of the same length.
1867 sequence. All sequences must be of the same length.
1862
1868
1863 Keyword arguments are passed verbatim to all objects called.
1869 Keyword arguments are passed verbatim to all objects called.
1864
1870
1865 This is Python code, so it's not nearly as fast as the builtin map()."""
1871 This is Python code, so it's not nearly as fast as the builtin map()."""
1866
1872
1867 out_list = []
1873 out_list = []
1868 idx = 0
1874 idx = 0
1869 for object in object_list:
1875 for object in object_list:
1870 try:
1876 try:
1871 handler = getattr(object, method)
1877 handler = getattr(object, method)
1872 except AttributeError:
1878 except AttributeError:
1873 out_list.append(None)
1879 out_list.append(None)
1874 else:
1880 else:
1875 if argseq:
1881 if argseq:
1876 args = map(lambda lst:lst[idx],argseq)
1882 args = map(lambda lst:lst[idx],argseq)
1877 #print 'ob',object,'hand',handler,'ar',args # dbg
1883 #print 'ob',object,'hand',handler,'ar',args # dbg
1878 out_list.append(handler(args,**kw))
1884 out_list.append(handler(args,**kw))
1879 else:
1885 else:
1880 out_list.append(handler(**kw))
1886 out_list.append(handler(**kw))
1881 idx += 1
1887 idx += 1
1882 return out_list
1888 return out_list
1883
1889
1884 #----------------------------------------------------------------------------
1890 #----------------------------------------------------------------------------
1885 def get_class_members(cls):
1891 def get_class_members(cls):
1886 ret = dir(cls)
1892 ret = dir(cls)
1887 if hasattr(cls,'__bases__'):
1893 if hasattr(cls,'__bases__'):
1888 for base in cls.__bases__:
1894 for base in cls.__bases__:
1889 ret.extend(get_class_members(base))
1895 ret.extend(get_class_members(base))
1890 return ret
1896 return ret
1891
1897
1892 #----------------------------------------------------------------------------
1898 #----------------------------------------------------------------------------
1893 def dir2(obj):
1899 def dir2(obj):
1894 """dir2(obj) -> list of strings
1900 """dir2(obj) -> list of strings
1895
1901
1896 Extended version of the Python builtin dir(), which does a few extra
1902 Extended version of the Python builtin dir(), which does a few extra
1897 checks, and supports common objects with unusual internals that confuse
1903 checks, and supports common objects with unusual internals that confuse
1898 dir(), such as Traits and PyCrust.
1904 dir(), such as Traits and PyCrust.
1899
1905
1900 This version is guaranteed to return only a list of true strings, whereas
1906 This version is guaranteed to return only a list of true strings, whereas
1901 dir() returns anything that objects inject into themselves, even if they
1907 dir() returns anything that objects inject into themselves, even if they
1902 are later not really valid for attribute access (many extension libraries
1908 are later not really valid for attribute access (many extension libraries
1903 have such bugs).
1909 have such bugs).
1904 """
1910 """
1905
1911
1906 # Start building the attribute list via dir(), and then complete it
1912 # Start building the attribute list via dir(), and then complete it
1907 # with a few extra special-purpose calls.
1913 # with a few extra special-purpose calls.
1908 words = dir(obj)
1914 words = dir(obj)
1909
1915
1910 if hasattr(obj,'__class__'):
1916 if hasattr(obj,'__class__'):
1911 words.append('__class__')
1917 words.append('__class__')
1912 words.extend(get_class_members(obj.__class__))
1918 words.extend(get_class_members(obj.__class__))
1913 #if '__base__' in words: 1/0
1919 #if '__base__' in words: 1/0
1914
1920
1915 # Some libraries (such as traits) may introduce duplicates, we want to
1921 # Some libraries (such as traits) may introduce duplicates, we want to
1916 # track and clean this up if it happens
1922 # track and clean this up if it happens
1917 may_have_dupes = False
1923 may_have_dupes = False
1918
1924
1919 # this is the 'dir' function for objects with Enthought's traits
1925 # this is the 'dir' function for objects with Enthought's traits
1920 if hasattr(obj, 'trait_names'):
1926 if hasattr(obj, 'trait_names'):
1921 try:
1927 try:
1922 words.extend(obj.trait_names())
1928 words.extend(obj.trait_names())
1923 may_have_dupes = True
1929 may_have_dupes = True
1924 except TypeError:
1930 except TypeError:
1925 # This will happen if `obj` is a class and not an instance.
1931 # This will happen if `obj` is a class and not an instance.
1926 pass
1932 pass
1927
1933
1928 # Support for PyCrust-style _getAttributeNames magic method.
1934 # Support for PyCrust-style _getAttributeNames magic method.
1929 if hasattr(obj, '_getAttributeNames'):
1935 if hasattr(obj, '_getAttributeNames'):
1930 try:
1936 try:
1931 words.extend(obj._getAttributeNames())
1937 words.extend(obj._getAttributeNames())
1932 may_have_dupes = True
1938 may_have_dupes = True
1933 except TypeError:
1939 except TypeError:
1934 # `obj` is a class and not an instance. Ignore
1940 # `obj` is a class and not an instance. Ignore
1935 # this error.
1941 # this error.
1936 pass
1942 pass
1937
1943
1938 if may_have_dupes:
1944 if may_have_dupes:
1939 # eliminate possible duplicates, as some traits may also
1945 # eliminate possible duplicates, as some traits may also
1940 # appear as normal attributes in the dir() call.
1946 # appear as normal attributes in the dir() call.
1941 words = list(set(words))
1947 words = list(set(words))
1942 words.sort()
1948 words.sort()
1943
1949
1944 # filter out non-string attributes which may be stuffed by dir() calls
1950 # filter out non-string attributes which may be stuffed by dir() calls
1945 # and poor coding in third-party modules
1951 # and poor coding in third-party modules
1946 return [w for w in words if isinstance(w, basestring)]
1952 return [w for w in words if isinstance(w, basestring)]
1947
1953
1948 #----------------------------------------------------------------------------
1954 #----------------------------------------------------------------------------
1949 def import_fail_info(mod_name,fns=None):
1955 def import_fail_info(mod_name,fns=None):
1950 """Inform load failure for a module."""
1956 """Inform load failure for a module."""
1951
1957
1952 if fns == None:
1958 if fns == None:
1953 warn("Loading of %s failed.\n" % (mod_name,))
1959 warn("Loading of %s failed.\n" % (mod_name,))
1954 else:
1960 else:
1955 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
1961 warn("Loading of %s from %s failed.\n" % (fns,mod_name))
1956
1962
1957 #----------------------------------------------------------------------------
1963 #----------------------------------------------------------------------------
1958 # Proposed popitem() extension, written as a method
1964 # Proposed popitem() extension, written as a method
1959
1965
1960
1966
1961 class NotGiven: pass
1967 class NotGiven: pass
1962
1968
1963 def popkey(dct,key,default=NotGiven):
1969 def popkey(dct,key,default=NotGiven):
1964 """Return dct[key] and delete dct[key].
1970 """Return dct[key] and delete dct[key].
1965
1971
1966 If default is given, return it if dct[key] doesn't exist, otherwise raise
1972 If default is given, return it if dct[key] doesn't exist, otherwise raise
1967 KeyError. """
1973 KeyError. """
1968
1974
1969 try:
1975 try:
1970 val = dct[key]
1976 val = dct[key]
1971 except KeyError:
1977 except KeyError:
1972 if default is NotGiven:
1978 if default is NotGiven:
1973 raise
1979 raise
1974 else:
1980 else:
1975 return default
1981 return default
1976 else:
1982 else:
1977 del dct[key]
1983 del dct[key]
1978 return val
1984 return val
1979
1985
1980 def wrap_deprecated(func, suggest = '<nothing>'):
1986 def wrap_deprecated(func, suggest = '<nothing>'):
1981 def newFunc(*args, **kwargs):
1987 def newFunc(*args, **kwargs):
1982 warnings.warn("Call to deprecated function %s, use %s instead" %
1988 warnings.warn("Call to deprecated function %s, use %s instead" %
1983 ( func.__name__, suggest),
1989 ( func.__name__, suggest),
1984 category=DeprecationWarning,
1990 category=DeprecationWarning,
1985 stacklevel = 2)
1991 stacklevel = 2)
1986 return func(*args, **kwargs)
1992 return func(*args, **kwargs)
1987 return newFunc
1993 return newFunc
1988
1994
1989 #*************************** end of file <genutils.py> **********************
1995 #*************************** end of file <genutils.py> **********************
1990
1996
General Comments 0
You need to be logged in to leave comments. Login now