##// END OF EJS Templates
use arg_split(...strict=False) in module_completer
Min RK -
Show More
@@ -1,318 +1,318 b''
1 """Implementations for various useful completers.
1 """Implementations for various useful completers.
2
2
3 These are all loaded by default by IPython.
3 These are all loaded by default by IPython.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2010-2011 The IPython Development Team.
6 # Copyright (C) 2010-2011 The IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the BSD License.
8 # Distributed under the terms of the BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib imports
18 # Stdlib imports
19 import glob
19 import glob
20 import inspect
20 import inspect
21 import os
21 import os
22 import re
22 import re
23 import sys
23 import sys
24
24
25 # Third-party imports
25 # Third-party imports
26 from time import time
26 from time import time
27 from zipimport import zipimporter
27 from zipimport import zipimporter
28
28
29 # Our own imports
29 # Our own imports
30 from IPython.core.completer import expand_user, compress_user
30 from IPython.core.completer import expand_user, compress_user
31 from IPython.core.error import TryNext
31 from IPython.core.error import TryNext
32 from IPython.utils import py3compat
32 from IPython.utils import py3compat
33 from IPython.utils._process_common import arg_split
33 from IPython.utils._process_common import arg_split
34
34
35 # FIXME: this should be pulled in with the right call via the component system
35 # FIXME: this should be pulled in with the right call via the component system
36 from IPython.core.ipapi import get as get_ipython
36 from IPython.core.ipapi import get as get_ipython
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Globals and constants
39 # Globals and constants
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 # Time in seconds after which the rootmodules will be stored permanently in the
42 # Time in seconds after which the rootmodules will be stored permanently in the
43 # ipython ip.db database (kept in the user's .ipython dir).
43 # ipython ip.db database (kept in the user's .ipython dir).
44 TIMEOUT_STORAGE = 2
44 TIMEOUT_STORAGE = 2
45
45
46 # Time in seconds after which we give up
46 # Time in seconds after which we give up
47 TIMEOUT_GIVEUP = 20
47 TIMEOUT_GIVEUP = 20
48
48
49 # Regular expression for the python import statement
49 # Regular expression for the python import statement
50 import_re = re.compile(r'.*(\.so|\.py[cod]?)$')
50 import_re = re.compile(r'.*(\.so|\.py[cod]?)$')
51
51
52 # RE for the ipython %run command (python + ipython scripts)
52 # RE for the ipython %run command (python + ipython scripts)
53 magic_run_re = re.compile(r'.*(\.ipy|\.py[w]?)$')
53 magic_run_re = re.compile(r'.*(\.ipy|\.py[w]?)$')
54
54
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56 # Local utilities
56 # Local utilities
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58
58
59 def module_list(path):
59 def module_list(path):
60 """
60 """
61 Return the list containing the names of the modules available in the given
61 Return the list containing the names of the modules available in the given
62 folder.
62 folder.
63 """
63 """
64
64
65 if os.path.isdir(path):
65 if os.path.isdir(path):
66 folder_list = os.listdir(path)
66 folder_list = os.listdir(path)
67 elif path.endswith('.egg'):
67 elif path.endswith('.egg'):
68 try:
68 try:
69 folder_list = [f for f in zipimporter(path)._files]
69 folder_list = [f for f in zipimporter(path)._files]
70 except:
70 except:
71 folder_list = []
71 folder_list = []
72 else:
72 else:
73 folder_list = []
73 folder_list = []
74
74
75 if not folder_list:
75 if not folder_list:
76 return []
76 return []
77
77
78 # A few local constants to be used in loops below
78 # A few local constants to be used in loops below
79 isfile = os.path.isfile
79 isfile = os.path.isfile
80 pjoin = os.path.join
80 pjoin = os.path.join
81 basename = os.path.basename
81 basename = os.path.basename
82
82
83 # Now find actual path matches for packages or modules
83 # Now find actual path matches for packages or modules
84 folder_list = [p for p in folder_list
84 folder_list = [p for p in folder_list
85 if isfile(pjoin(path, p,'__init__.py'))
85 if isfile(pjoin(path, p,'__init__.py'))
86 or import_re.match(p) ]
86 or import_re.match(p) ]
87
87
88 return [basename(p).split('.')[0] for p in folder_list]
88 return [basename(p).split('.')[0] for p in folder_list]
89
89
90 def get_root_modules():
90 def get_root_modules():
91 """
91 """
92 Returns a list containing the names of all the modules available in the
92 Returns a list containing the names of all the modules available in the
93 folders of the pythonpath.
93 folders of the pythonpath.
94 """
94 """
95 ip = get_ipython()
95 ip = get_ipython()
96
96
97 if 'rootmodules' in ip.db:
97 if 'rootmodules' in ip.db:
98 return ip.db['rootmodules']
98 return ip.db['rootmodules']
99
99
100 t = time()
100 t = time()
101 store = False
101 store = False
102 modules = list(sys.builtin_module_names)
102 modules = list(sys.builtin_module_names)
103 for path in sys.path:
103 for path in sys.path:
104 modules += module_list(path)
104 modules += module_list(path)
105 if time() - t >= TIMEOUT_STORAGE and not store:
105 if time() - t >= TIMEOUT_STORAGE and not store:
106 store = True
106 store = True
107 print("\nCaching the list of root modules, please wait!")
107 print("\nCaching the list of root modules, please wait!")
108 print("(This will only be done once - type '%rehashx' to "
108 print("(This will only be done once - type '%rehashx' to "
109 "reset cache!)\n")
109 "reset cache!)\n")
110 sys.stdout.flush()
110 sys.stdout.flush()
111 if time() - t > TIMEOUT_GIVEUP:
111 if time() - t > TIMEOUT_GIVEUP:
112 print("This is taking too long, we give up.\n")
112 print("This is taking too long, we give up.\n")
113 ip.db['rootmodules'] = []
113 ip.db['rootmodules'] = []
114 return []
114 return []
115
115
116 modules = set(modules)
116 modules = set(modules)
117 if '__init__' in modules:
117 if '__init__' in modules:
118 modules.remove('__init__')
118 modules.remove('__init__')
119 modules = list(modules)
119 modules = list(modules)
120 if store:
120 if store:
121 ip.db['rootmodules'] = modules
121 ip.db['rootmodules'] = modules
122 return modules
122 return modules
123
123
124
124
125 def is_importable(module, attr, only_modules):
125 def is_importable(module, attr, only_modules):
126 if only_modules:
126 if only_modules:
127 return inspect.ismodule(getattr(module, attr))
127 return inspect.ismodule(getattr(module, attr))
128 else:
128 else:
129 return not(attr[:2] == '__' and attr[-2:] == '__')
129 return not(attr[:2] == '__' and attr[-2:] == '__')
130
130
131
131
132 def try_import(mod, only_modules=False):
132 def try_import(mod, only_modules=False):
133 try:
133 try:
134 m = __import__(mod)
134 m = __import__(mod)
135 except:
135 except:
136 return []
136 return []
137 mods = mod.split('.')
137 mods = mod.split('.')
138 for module in mods[1:]:
138 for module in mods[1:]:
139 m = getattr(m, module)
139 m = getattr(m, module)
140
140
141 m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__
141 m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__
142
142
143 completions = []
143 completions = []
144 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
144 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
145 completions.extend( [attr for attr in dir(m) if
145 completions.extend( [attr for attr in dir(m) if
146 is_importable(m, attr, only_modules)])
146 is_importable(m, attr, only_modules)])
147
147
148 completions.extend(getattr(m, '__all__', []))
148 completions.extend(getattr(m, '__all__', []))
149 if m_is_init:
149 if m_is_init:
150 completions.extend(module_list(os.path.dirname(m.__file__)))
150 completions.extend(module_list(os.path.dirname(m.__file__)))
151 completions = set(completions)
151 completions = set(completions)
152 if '__init__' in completions:
152 if '__init__' in completions:
153 completions.remove('__init__')
153 completions.remove('__init__')
154 return list(completions)
154 return list(completions)
155
155
156
156
157 #-----------------------------------------------------------------------------
157 #-----------------------------------------------------------------------------
158 # Completion-related functions.
158 # Completion-related functions.
159 #-----------------------------------------------------------------------------
159 #-----------------------------------------------------------------------------
160
160
161 def quick_completer(cmd, completions):
161 def quick_completer(cmd, completions):
162 """ Easily create a trivial completer for a command.
162 """ Easily create a trivial completer for a command.
163
163
164 Takes either a list of completions, or all completions in string (that will
164 Takes either a list of completions, or all completions in string (that will
165 be split on whitespace).
165 be split on whitespace).
166
166
167 Example::
167 Example::
168
168
169 [d:\ipython]|1> import ipy_completers
169 [d:\ipython]|1> import ipy_completers
170 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
170 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
171 [d:\ipython]|3> foo b<TAB>
171 [d:\ipython]|3> foo b<TAB>
172 bar baz
172 bar baz
173 [d:\ipython]|3> foo ba
173 [d:\ipython]|3> foo ba
174 """
174 """
175
175
176 if isinstance(completions, basestring):
176 if isinstance(completions, basestring):
177 completions = completions.split()
177 completions = completions.split()
178
178
179 def do_complete(self, event):
179 def do_complete(self, event):
180 return completions
180 return completions
181
181
182 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
182 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
183
183
184
184
185 def module_completion(line):
185 def module_completion(line):
186 """
186 """
187 Returns a list containing the completion possibilities for an import line.
187 Returns a list containing the completion possibilities for an import line.
188
188
189 The line looks like this :
189 The line looks like this :
190 'import xml.d'
190 'import xml.d'
191 'from xml.dom import'
191 'from xml.dom import'
192 """
192 """
193
193
194 words = line.split(' ')
194 words = line.split(' ')
195 nwords = len(words)
195 nwords = len(words)
196
196
197 # from whatever <tab> -> 'import '
197 # from whatever <tab> -> 'import '
198 if nwords == 3 and words[0] == 'from':
198 if nwords == 3 and words[0] == 'from':
199 return ['import ']
199 return ['import ']
200
200
201 # 'from xy<tab>' or 'import xy<tab>'
201 # 'from xy<tab>' or 'import xy<tab>'
202 if nwords < 3 and (words[0] in ['import','from']) :
202 if nwords < 3 and (words[0] in ['import','from']) :
203 if nwords == 1:
203 if nwords == 1:
204 return get_root_modules()
204 return get_root_modules()
205 mod = words[1].split('.')
205 mod = words[1].split('.')
206 if len(mod) < 2:
206 if len(mod) < 2:
207 return get_root_modules()
207 return get_root_modules()
208 completion_list = try_import('.'.join(mod[:-1]), True)
208 completion_list = try_import('.'.join(mod[:-1]), True)
209 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
209 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
210
210
211 # 'from xyz import abc<tab>'
211 # 'from xyz import abc<tab>'
212 if nwords >= 3 and words[0] == 'from':
212 if nwords >= 3 and words[0] == 'from':
213 mod = words[1]
213 mod = words[1]
214 return try_import(mod)
214 return try_import(mod)
215
215
216 #-----------------------------------------------------------------------------
216 #-----------------------------------------------------------------------------
217 # Completers
217 # Completers
218 #-----------------------------------------------------------------------------
218 #-----------------------------------------------------------------------------
219 # These all have the func(self, event) signature to be used as custom
219 # These all have the func(self, event) signature to be used as custom
220 # completers
220 # completers
221
221
222 def module_completer(self,event):
222 def module_completer(self,event):
223 """Give completions after user has typed 'import ...' or 'from ...'"""
223 """Give completions after user has typed 'import ...' or 'from ...'"""
224
224
225 # This works in all versions of python. While 2.5 has
225 # This works in all versions of python. While 2.5 has
226 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
226 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
227 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
227 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
228 # of possibly problematic side effects.
228 # of possibly problematic side effects.
229 # This search the folders in the sys.path for available modules.
229 # This search the folders in the sys.path for available modules.
230
230
231 return module_completion(event.line)
231 return module_completion(event.line)
232
232
233 # FIXME: there's a lot of logic common to the run, cd and builtin file
233 # FIXME: there's a lot of logic common to the run, cd and builtin file
234 # completers, that is currently reimplemented in each.
234 # completers, that is currently reimplemented in each.
235
235
236 def magic_run_completer(self, event):
236 def magic_run_completer(self, event):
237 """Complete files that end in .py or .ipy for the %run command.
237 """Complete files that end in .py or .ipy for the %run command.
238 """
238 """
239 comps = arg_split(event.line)
239 comps = arg_split(event.line, strict=False)
240 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
240 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
241
241
242 #print("\nev=", event) # dbg
242 #print("\nev=", event) # dbg
243 #print("rp=", relpath) # dbg
243 #print("rp=", relpath) # dbg
244 #print('comps=', comps) # dbg
244 #print('comps=', comps) # dbg
245
245
246 lglob = glob.glob
246 lglob = glob.glob
247 isdir = os.path.isdir
247 isdir = os.path.isdir
248 relpath, tilde_expand, tilde_val = expand_user(relpath)
248 relpath, tilde_expand, tilde_val = expand_user(relpath)
249
249
250 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
250 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
251
251
252 # Find if the user has already typed the first filename, after which we
252 # Find if the user has already typed the first filename, after which we
253 # should complete on all files, since after the first one other files may
253 # should complete on all files, since after the first one other files may
254 # be arguments to the input script.
254 # be arguments to the input script.
255
255
256 if filter(magic_run_re.match, comps):
256 if filter(magic_run_re.match, comps):
257 pys = [f.replace('\\','/') for f in lglob('*')]
257 pys = [f.replace('\\','/') for f in lglob('*')]
258 else:
258 else:
259 pys = [f.replace('\\','/')
259 pys = [f.replace('\\','/')
260 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
260 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
261 lglob(relpath + '*.pyw')]
261 lglob(relpath + '*.pyw')]
262 #print('run comp:', dirs+pys) # dbg
262 #print('run comp:', dirs+pys) # dbg
263 return [compress_user(p, tilde_expand, tilde_val) for p in dirs+pys]
263 return [compress_user(p, tilde_expand, tilde_val) for p in dirs+pys]
264
264
265
265
266 def cd_completer(self, event):
266 def cd_completer(self, event):
267 """Completer function for cd, which only returns directories."""
267 """Completer function for cd, which only returns directories."""
268 ip = get_ipython()
268 ip = get_ipython()
269 relpath = event.symbol
269 relpath = event.symbol
270
270
271 #print(event) # dbg
271 #print(event) # dbg
272 if event.line.endswith('-b') or ' -b ' in event.line:
272 if event.line.endswith('-b') or ' -b ' in event.line:
273 # return only bookmark completions
273 # return only bookmark completions
274 bkms = self.db.get('bookmarks', None)
274 bkms = self.db.get('bookmarks', None)
275 if bkms:
275 if bkms:
276 return bkms.keys()
276 return bkms.keys()
277 else:
277 else:
278 return []
278 return []
279
279
280 if event.symbol == '-':
280 if event.symbol == '-':
281 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
281 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
282 # jump in directory history by number
282 # jump in directory history by number
283 fmt = '-%0' + width_dh +'d [%s]'
283 fmt = '-%0' + width_dh +'d [%s]'
284 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
284 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
285 if len(ents) > 1:
285 if len(ents) > 1:
286 return ents
286 return ents
287 return []
287 return []
288
288
289 if event.symbol.startswith('--'):
289 if event.symbol.startswith('--'):
290 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
290 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
291
291
292 # Expand ~ in path and normalize directory separators.
292 # Expand ~ in path and normalize directory separators.
293 relpath, tilde_expand, tilde_val = expand_user(relpath)
293 relpath, tilde_expand, tilde_val = expand_user(relpath)
294 relpath = relpath.replace('\\','/')
294 relpath = relpath.replace('\\','/')
295
295
296 found = []
296 found = []
297 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
297 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
298 if os.path.isdir(f)]:
298 if os.path.isdir(f)]:
299 if ' ' in d:
299 if ' ' in d:
300 # we don't want to deal with any of that, complex code
300 # we don't want to deal with any of that, complex code
301 # for this is elsewhere
301 # for this is elsewhere
302 raise TryNext
302 raise TryNext
303
303
304 found.append(d)
304 found.append(d)
305
305
306 if not found:
306 if not found:
307 if os.path.isdir(relpath):
307 if os.path.isdir(relpath):
308 return [compress_user(relpath, tilde_expand, tilde_val)]
308 return [compress_user(relpath, tilde_expand, tilde_val)]
309
309
310 # if no completions so far, try bookmarks
310 # if no completions so far, try bookmarks
311 bks = self.db.get('bookmarks',{}).iterkeys()
311 bks = self.db.get('bookmarks',{}).iterkeys()
312 bkmatches = [s for s in bks if s.startswith(event.symbol)]
312 bkmatches = [s for s in bks if s.startswith(event.symbol)]
313 if bkmatches:
313 if bkmatches:
314 return bkmatches
314 return bkmatches
315
315
316 raise TryNext
316 raise TryNext
317
317
318 return [compress_user(p, tilde_expand, tilde_val) for p in found]
318 return [compress_user(p, tilde_expand, tilde_val) for p in found]
General Comments 0
You need to be logged in to leave comments. Login now