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