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