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