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