##// END OF EJS Templates
module completer looks inside .egg zip files, close #196.
Ville M. Vainio -
Show More

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

@@ -1,359 +1,365 b''
1 1 #!/usr/bin/env python
2 2
3 3 """ Implementations for various useful completers
4 4
5 5 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
6 6 but the basic idea is to do:
7 7
8 8 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
9 9
10 10 """
11 11 import IPython.ipapi
12 12 import glob,os,shlex,sys
13 13 import inspect
14 14 from time import time
15 from zipimport import zipimporter
15 16 ip = IPython.ipapi.get()
16 17
17 18 try:
18 19 set
19 20 except:
20 21 from sets import Set as set
21 22
22 23 TIMEOUT_STORAGE = 3 #Time in seconds after which the rootmodules will be stored
23 24 TIMEOUT_GIVEUP = 20 #Time in seconds after which we give up
24 25
25 26 def quick_completer(cmd, completions):
26 27 """ Easily create a trivial completer for a command.
27 28
28 29 Takes either a list of completions, or all completions in string
29 30 (that will be split on whitespace)
30 31
31 32 Example::
32 33
33 34 [d:\ipython]|1> import ipy_completers
34 35 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
35 36 [d:\ipython]|3> foo b<TAB>
36 37 bar baz
37 38 [d:\ipython]|3> foo ba
38 39 """
39 40 if isinstance(completions, basestring):
40 41
41 42 completions = completions.split()
42 43 def do_complete(self,event):
43 44 return completions
44 45
45 46 ip.set_hook('complete_command',do_complete, str_key = cmd)
46 47
47 48 def getRootModules():
48 49 """
49 50 Returns a list containing the names of all the modules available in the
50 51 folders of the pythonpath.
51 52 """
52 53 modules = []
53 54 if ip.db.has_key('rootmodules'):
54 55 return ip.db['rootmodules']
55 56 t = time()
56 57 store = False
57 58 for path in sys.path:
58 59 modules += moduleList(path)
59 60 if time() - t >= TIMEOUT_STORAGE and not store:
60 61 store = True
61 62 print "\nCaching the list of root modules, please wait!"
62 63 print "(This will only be done once - type '%rehashx' to " + \
63 64 "reset cache!)"
64 65 print
65 66 if time() - t > TIMEOUT_GIVEUP:
66 67 print "This is taking too long, we give up."
67 68 print
68 69 ip.db['rootmodules'] = []
69 70 return []
70 71
71 72 modules += sys.builtin_module_names
72 73
73 74 modules = list(set(modules))
74 75 if '__init__' in modules:
75 76 modules.remove('__init__')
76 77 modules = list(set(modules))
77 78 if store:
78 79 ip.db['rootmodules'] = modules
79 80 return modules
80 81
81 82 def moduleList(path):
82 83 """
83 84 Return the list containing the names of the modules available in the given
84 85 folder.
85 86 """
86 87
87 88 if os.path.isdir(path):
88 89 folder_list = os.listdir(path)
90 elif path.endswith('.egg'):
91 try:
92 folder_list = [f for f in zipimporter(path)._files]
93 except:
94 folder_list = []
89 95 else:
90 96 folder_list = []
91 97 #folder_list = glob.glob(os.path.join(path,'*'))
92 98 folder_list = [p for p in folder_list \
93 99 if os.path.exists(os.path.join(path, p,'__init__.py'))\
94 100 or p[-3:] in ('.py','.so')\
95 101 or p[-4:] in ('.pyc','.pyo','.pyd')]
96 102
97 103 folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
98 104 return folder_list
99 105
100 106 def moduleCompletion(line):
101 107 """
102 108 Returns a list containing the completion possibilities for an import line.
103 109 The line looks like this :
104 110 'import xml.d'
105 111 'from xml.dom import'
106 112 """
107 113 def tryImport(mod, only_modules=False):
108 114 def isImportable(module, attr):
109 115 if only_modules:
110 116 return inspect.ismodule(getattr(module, attr))
111 117 else:
112 118 return not(attr[:2] == '__' and attr[-2:] == '__')
113 119 try:
114 120 m = __import__(mod)
115 121 except:
116 122 return []
117 123 mods = mod.split('.')
118 124 for module in mods[1:]:
119 125 m = getattr(m,module)
120 126 if (not hasattr(m, '__file__')) or (not only_modules) or\
121 127 (hasattr(m, '__file__') and '__init__' in m.__file__):
122 128 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
123 129 completion_list.extend(getattr(m,'__all__',[]))
124 130 if hasattr(m, '__file__') and '__init__' in m.__file__:
125 131 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
126 132 completion_list = list(set(completion_list))
127 133 if '__init__' in completion_list:
128 134 completion_list.remove('__init__')
129 135 return completion_list
130 136
131 137 words = line.split(' ')
132 138 if len(words) == 3 and words[0] == 'from':
133 139 return ['import ']
134 140 if len(words) < 3 and (words[0] in ['import','from']) :
135 141 if len(words) == 1:
136 142 return getRootModules()
137 143 mod = words[1].split('.')
138 144 if len(mod) < 2:
139 145 return getRootModules()
140 146 completion_list = tryImport('.'.join(mod[:-1]), True)
141 147 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
142 148 return completion_list
143 149 if len(words) >= 3 and words[0] == 'from':
144 150 mod = words[1]
145 151 return tryImport(mod)
146 152
147 153 def vcs_completer(commands, event):
148 154 """ utility to make writing typical version control app completers easier
149 155
150 156 VCS command line apps typically have the format:
151 157
152 158 [sudo ]PROGNAME [help] [command] file file...
153 159
154 160 """
155 161
156 162
157 163 cmd_param = event.line.split()
158 164 if event.line.endswith(' '):
159 165 cmd_param.append('')
160 166
161 167 if cmd_param[0] == 'sudo':
162 168 cmd_param = cmd_param[1:]
163 169
164 170 if len(cmd_param) == 2 or 'help' in cmd_param:
165 171 return commands.split()
166 172
167 173 return ip.IP.Completer.file_matches(event.symbol)
168 174
169 175
170 176 pkg_cache = None
171 177
172 178 def module_completer(self,event):
173 179 """ Give completions after user has typed 'import ...' or 'from ...'"""
174 180
175 181 # This works in all versions of python. While 2.5 has
176 182 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
177 183 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
178 184 # of possibly problematic side effects.
179 185 # This search the folders in the sys.path for available modules.
180 186
181 187 return moduleCompletion(event.line)
182 188
183 189
184 190 svn_commands = """\
185 191 add blame praise annotate ann cat checkout co cleanup commit ci copy
186 192 cp delete del remove rm diff di export help ? h import info list ls
187 193 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
188 194 pe propget pget pg proplist plist pl propset pset ps resolved revert
189 195 status stat st switch sw unlock update
190 196 """
191 197
192 198 def svn_completer(self,event):
193 199 return vcs_completer(svn_commands, event)
194 200
195 201
196 202 hg_commands = """
197 203 add addremove annotate archive backout branch branches bundle cat
198 204 clone commit copy diff export grep heads help identify import incoming
199 205 init locate log manifest merge outgoing parents paths pull push
200 206 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
201 207 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
202 208 qselect qseries qtop qunapplied recover remove rename revert rollback
203 209 root serve showconfig status strip tag tags tip unbundle update verify
204 210 version
205 211 """
206 212
207 213 def hg_completer(self,event):
208 214 """ Completer for mercurial commands """
209 215
210 216 return vcs_completer(hg_commands, event)
211 217
212 218
213 219
214 220 __bzr_commands = None
215 221
216 222 def bzr_commands():
217 223 global __bzr_commands
218 224 if __bzr_commands is not None:
219 225 return __bzr_commands
220 226 out = os.popen('bzr help commands')
221 227 __bzr_commands = [l.split()[0] for l in out]
222 228 return __bzr_commands
223 229
224 230 def bzr_completer(self,event):
225 231 """ Completer for bazaar commands """
226 232 cmd_param = event.line.split()
227 233 if event.line.endswith(' '):
228 234 cmd_param.append('')
229 235
230 236 if len(cmd_param) > 2:
231 237 cmd = cmd_param[1]
232 238 param = cmd_param[-1]
233 239 output_file = (param == '--output=')
234 240 if cmd == 'help':
235 241 return bzr_commands()
236 242 elif cmd in ['bundle-revisions','conflicts',
237 243 'deleted','nick','register-branch',
238 244 'serve','unbind','upgrade','version',
239 245 'whoami'] and not output_file:
240 246 return []
241 247 else:
242 248 # the rest are probably file names
243 249 return ip.IP.Completer.file_matches(event.symbol)
244 250
245 251 return bzr_commands()
246 252
247 253
248 254 def shlex_split(x):
249 255 """Helper function to split lines into segments."""
250 256 #shlex.split raise exception if syntax error in sh syntax
251 257 #for example if no closing " is found. This function keeps dropping
252 258 #the last character of the line until shlex.split does not raise
253 259 #exception. Adds end of the line to the result of shlex.split
254 260 #example: %run "c:/python -> ['%run','"c:/python']
255 261 endofline=[]
256 262 while x!="":
257 263 try:
258 264 comps=shlex.split(x)
259 265 if len(endofline)>=1:
260 266 comps.append("".join(endofline))
261 267 return comps
262 268 except ValueError:
263 269 endofline=[x[-1:]]+endofline
264 270 x=x[:-1]
265 271 return ["".join(endofline)]
266 272
267 273 def runlistpy(self, event):
268 274 comps = shlex_split(event.line)
269 275 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
270 276
271 277 #print "\nev=",event # dbg
272 278 #print "rp=",relpath # dbg
273 279 #print 'comps=',comps # dbg
274 280
275 281 lglob = glob.glob
276 282 isdir = os.path.isdir
277 283 if relpath.startswith('~'):
278 284 relpath = os.path.expanduser(relpath)
279 285 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
280 286 if isdir(f)]
281 287
282 288 # Find if the user has already typed the first filename, after which we
283 289 # should complete on all files, since after the first one other files may
284 290 # be arguments to the input script.
285 291 #filter(
286 292 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy') or
287 293 f.endswith('.pyw'),comps):
288 294 pys = [f.replace('\\','/') for f in lglob('*')]
289 295 else:
290 296 pys = [f.replace('\\','/')
291 297 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
292 298 lglob(relpath + '*.pyw')]
293 299 return dirs + pys
294 300
295 301
296 302 def cd_completer(self, event):
297 303 relpath = event.symbol
298 304 #print event # dbg
299 305 if '-b' in event.line:
300 306 # return only bookmark completions
301 307 bkms = self.db.get('bookmarks',{})
302 308 return bkms.keys()
303 309
304 310
305 311 if event.symbol == '-':
306 312 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
307 313 # jump in directory history by number
308 314 fmt = '-%0' + width_dh +'d [%s]'
309 315 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
310 316 if len(ents) > 1:
311 317 return ents
312 318 return []
313 319
314 320 if relpath.startswith('~'):
315 321 relpath = os.path.expanduser(relpath).replace('\\','/')
316 322 found = []
317 323 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
318 324 if os.path.isdir(f)]:
319 325 if ' ' in d:
320 326 # we don't want to deal with any of that, complex code
321 327 # for this is elsewhere
322 328 raise IPython.ipapi.TryNext
323 329 found.append( d )
324 330
325 331 if not found:
326 332 if os.path.isdir(relpath):
327 333 return [relpath]
328 334 raise IPython.ipapi.TryNext
329 335 return found
330 336
331 337 def apt_get_packages(prefix):
332 338 out = os.popen('apt-cache pkgnames')
333 339 for p in out:
334 340 if p.startswith(prefix):
335 341 yield p.rstrip()
336 342
337 343
338 344 apt_commands = """\
339 345 update upgrade install remove purge source build-dep dist-upgrade
340 346 dselect-upgrade clean autoclean check"""
341 347
342 348 def apt_completer(self, event):
343 349 """ Completer for apt-get (uses apt-cache internally)
344 350
345 351 """
346 352
347 353
348 354 cmd_param = event.line.split()
349 355 if event.line.endswith(' '):
350 356 cmd_param.append('')
351 357
352 358 if cmd_param[0] == 'sudo':
353 359 cmd_param = cmd_param[1:]
354 360
355 361 if len(cmd_param) == 2 or 'help' in cmd_param:
356 362 return apt_commands.split()
357 363
358 364 return list(apt_get_packages(event.symbol))
359 365
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now