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