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