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