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