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