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