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