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