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